> ## Documentation Index
> Fetch the complete documentation index at: https://docs.routor.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Image Understanding to Your App

> Send an image alongside your prompt and Routor automatically narrows to vision-capable models.

Routor's `/v1/chat/completions` endpoint accepts images the same way OpenAI's API does - as an `image_url` content part on a message. You don't set a flag or pick a vision model yourself; attaching an image is what tells Routor the request needs one.

***

## How It Works

A normal text-only request sends `content` as a plain string. To attach an image, send `content` as an array of parts instead - one `text` part for your question, one `image_url` part per image:

```json theme={null}
{
  "model": "auto",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "What is this a photo of?" },
        { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
      ]
    }
  ]
}
```

`image_url.url` can be a `data:` URI (base64-encoded, for a local file) or a plain `https://` URL Routor can fetch.

The moment Routor sees an `image_url` part, it filters the candidate model pool down to vision-capable models before anything else - the same difficulty scoring still happens (a one-word caption request and a "explain everything happening in this diagram" request won't land on the same tier), but only within the models that can actually read images. You'll see `category: "vision"` in the routing decision on the response.

***

## Example - Node.js

```javascript theme={null}
import OpenAI from "openai";
import { readFileSync } from "fs";

const client = new OpenAI({
  apiKey: process.env.ROUTOR_API_KEY,
  baseURL: "https://api.routor.io/v1",
});

const imageBase64 = readFileSync("receipt.png").toString("base64");

const response = await client.chat.completions.create({
  model: "auto",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "What's the total on this receipt?" },
        { type: "image_url", image_url: { url: `data:image/png;base64,${imageBase64}` } },
      ],
    },
  ],
});

console.log(response.choices[0].message.content);
console.log(response.routor); // { model: ..., tier: ..., category: "vision", ... }
```

***

## Multiple Images, One Message

Add more `image_url` parts to the same `content` array - useful for "compare these two" or "here's the policy, and here's my receipt" style requests where the answer depends on more than one image together:

```json theme={null}
"content": [
  { "type": "text", "text": "Based on this return policy and my receipt, am I still eligible?" },
  { "type": "image_url", "image_url": { "url": "data:image/png;base64,...(policy screenshot)..." } },
  { "type": "image_url", "image_url": { "url": "data:image/png;base64,...(receipt)..." } }
]
```

Routor scores the combined request as one prompt - more context and a harder question (a decision, not just a description) can move it to a higher tier, same as it would for a text-only request with equivalent complexity.

***

## Attachment Limits

Vision-capable models have per-model caps on image count and payload size (Routor enforces these silently and picks a model that fits your request). If you're sending several large images, keep an eye on total request size - very large payloads add real upload time before Routor's routing decision even starts.

***

## Where to Go Next

* [Build your first app](nodejs-first-app) - the text-only starting point this guide builds on
* [How routing decisions are made](../how-it-works) - the general scoring mechanics
* [Set up a routing profile](../playground/create-profile) - require vision capability on every request through a given key, instead of relying on it being detected per-message
