# Create a moodboard

> Analyse 10 to 60 images into a moodboard — its palette, fabrics, prints, key items and keywords — delivered as a `moodboard:` record you can read, and pass to generation.

**Endpoint:** `POST https://api.refabric.com/v1/tasks/moodboard.create` · **Task:** `moodboard.create` · **Scope:** `tasks:run` · **Category:** Records & libraries

Send the images that set a direction — runway looks, a lookbook, your own photos — and get one moodboard back: the palette, the fabrics and prints found in the images, the key items and keywords. Pass it to generation and new designs follow it.

## Quick start

```python
import os
import time
import requests

headers = {"Authorization": f"Key {os.environ['REFABRIC_API_KEY']}"}

job = requests.post(
    "https://api.refabric.com/v1/tasks/moodboard.create",
    headers=headers,
    json={
        "images": [
            "https://example.com/looks/1.jpg",
            "https://example.com/looks/2.jpg",
            "https://example.com/looks/3.jpg",
            "https://example.com/looks/4.jpg",
            "https://example.com/looks/5.jpg",
            "https://example.com/looks/6.jpg",
            "https://example.com/looks/7.jpg",
            "https://example.com/looks/8.jpg",
            "https://example.com/looks/9.jpg",
            "art:9f3c01",
        ],
    },
).json()
print(job["job_id"])

while True:
    status = requests.get(job["status_url"], headers=headers).json()
    if status["lifecycle"] == "terminal":
        break
    time.sleep(5)

print(requests.get(job["result_url"], headers=headers).json())
```

```javascript
const headers = {
  Authorization: `Key ${process.env.REFABRIC_API_KEY}`,
  "Content-Type": "application/json",
};

const job = await fetch("https://api.refabric.com/v1/tasks/moodboard.create", {
  method: "POST",
  headers,
  body: JSON.stringify({
    "images": [
      "https://example.com/looks/1.jpg",
      "https://example.com/looks/2.jpg",
      "https://example.com/looks/3.jpg",
      "https://example.com/looks/4.jpg",
      "https://example.com/looks/5.jpg",
      "https://example.com/looks/6.jpg",
      "https://example.com/looks/7.jpg",
      "https://example.com/looks/8.jpg",
      "https://example.com/looks/9.jpg",
      "art:9f3c01"
    ]
  }),
}).then((r) => r.json());
console.log(job.job_id);

let status = job;
while (status.lifecycle !== "terminal") {
  await new Promise((r) => setTimeout(r, 5000));
  status = await fetch(job.status_url, { headers }).then((r) => r.json());
}

console.log(await fetch(job.result_url, { headers }).then((r) => r.json()));
```

```bash
curl -X POST "https://api.refabric.com/v1/tasks/moodboard.create" \
  -H "Authorization: Key $REFABRIC_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Prefer: wait=60" \
  -d '{"images":["https://example.com/looks/1.jpg","https://example.com/looks/2.jpg","https://example.com/looks/3.jpg","https://example.com/looks/4.jpg","https://example.com/looks/5.jpg","https://example.com/looks/6.jpg","https://example.com/looks/7.jpg","https://example.com/looks/8.jpg","https://example.com/looks/9.jpg","art:9f3c01"]}'
```

## Input schema

- `images` (array<string>, _required_, at least 10 items, at most 60 items) — 10 to 60 images to analyse, each one string. Every image is analysed as a look. An image that cannot be read is left out of the board and named in the job's `summary.warnings` (`image_not_fetchable`); it is still charged, as the price is set by the number of images sent.
  Example: `["https://example.com/looks/1.jpg","https://example.com/looks/2.jpg","https://example.com/looks/3.jpg","https://example.com/looks/4.jpg","https://example.com/looks/5.jpg","https://example.com/looks/6.jpg","https://example.com/looks/7.jpg","https://example.com/looks/8.jpg","https://example.com/looks/9.jpg","art:9f3c01"]`
- `name` (string, _optional_) — The moodboard's name, as it appears in your moodboard list. Omitted: "Untitled Moodboard".
  Example: `SS27 resort`
- `items` (array<object>, _optional_, at most 120 items) — Optional: your own fabrics and prints, each {type, url, name}, up to 60 of each type — the same shape as a moodboard's `data.items`. Sending any item of a type REPLACES that section of the analysis: the board's fabrics (or prints) are then exactly yours, not analysed, with no swatch made. Send none of a type to have it found in the images.
  Example: `[{"type":"fabric","url":"https://files.example.com/moodboard.create/1.jpg","name":"washed linen"},{"type":"print","url":"https://files.example.com/moodboard.create/2.png"}]`
  - `type` (string, _required_) — What one part (`items[]`) of a record is.
    Values: `fabric` (A fabric: its swatch or a photo of it. When the image is a fabric swatch or a photo of a fabric.); `print` (A print or pattern. When the image is a print or pattern.)
    Example: `fabric`
  - `url` (string, _required_, pattern: ^(https://|art:|file:).+$) — Any image, as one string: a URL, a file from an earlier result (`art:…` or its url), an upload (`file:…`) or a part of a record (an `items[].url` of its `data`).
    Example: `https://files.example.com/moodboard.create/1.jpg`
  - `name` (string, _optional_, max length 80) — The item's name, shown on the board. Omitted: your library label for that image, when it has one.
    Example: `washed linen`
- `colours` (array<object>, _optional_, at most 10 items) — Optional: up to 10 colours of your own, each {hex} (a colour from a moodboard's `data.colours` may be sent as it is). The colours you send become the palette later tasks use; the file's `colours` show the palette read from the images.
  Example: `[{"hex":"#E8D9C4"},{"hex":"#2F4A3A"}]`
  - `hex` (string, _required_, pattern: ^#[0-9A-Fa-f]{6}$) — The colour as #RRGGBB, like "#E8D9C4".
  - `name` (string, _optional_) — A name for the colour.
  - `pantone` (string, _optional_) — Its Pantone code, when known.
- `kind` (string, _optional_, default: `trend`) — What kind of moodboard to make from the images. A trend board reads every image as a look — its palette, fabrics, prints, key items and keywords — which is what most boards are made for, and it takes your own fabrics, prints and colours.
  Values: `trend` (A trend moodboard: each look is captioned and analysed into its palette, fabrics, prints, key items and keywords. You may send your own fabrics, prints and colours.); `brand_dna` (A brand DNA moodboard: what the images say about one brand's recurring style. Best on a brand's own lookbook. Takes no `items` or `colours`. When the images are one brand's own lookbook and you want its recurring style.)
  Example: `trend`

## Required-fields example

```json
{
  "images": [
    "https://example.com/looks/1.jpg",
    "https://example.com/looks/2.jpg",
    "https://example.com/looks/3.jpg",
    "https://example.com/looks/4.jpg",
    "https://example.com/looks/5.jpg",
    "https://example.com/looks/6.jpg",
    "https://example.com/looks/7.jpg",
    "https://example.com/looks/8.jpg",
    "https://example.com/looks/9.jpg",
    "art:9f3c01"
  ]
}
```

## Full example

```json
{
  "kind": "trend",
  "name": "SS27 resort",
  "images": [
    "https://example.com/looks/1.jpg",
    "https://example.com/looks/2.jpg",
    "https://example.com/looks/3.jpg",
    "https://example.com/looks/4.jpg",
    "https://example.com/looks/5.jpg",
    "https://example.com/looks/6.jpg",
    "https://example.com/looks/7.jpg",
    "https://example.com/looks/8.jpg",
    "https://example.com/looks/9.jpg",
    "art:9f3c01"
  ],
  "items": [
    {
      "type": "fabric",
      "url": "https://files.example.com/moodboard.create/1.jpg",
      "name": "washed linen"
    },
    {
      "type": "print",
      "url": "https://files.example.com/moodboard.create/2.png"
    }
  ],
  "colours": [
    {
      "hex": "#E8D9C4"
    },
    {
      "hex": "#2F4A3A"
    }
  ]
}
```

## Output schema

- `files` (array<object>, _optional_) — One file: the moodboard record, `moodboard:<id>` — the same line `GET /v1/files/moodboard:<id>` answers, its `url` the cover and its `data` the record (name · description · status · colours · items · keywords). Pass the handle to `image.generate` in `moodboards`.
- `summary` (object, _optional_) — How many images were sent and analysed, and which could not be read.
  - `requested` (integer, _optional_) — Images sent (and charged).
  - `delivered` (integer, _optional_) — Images on the board that came back analysed.
  - `warnings` (array<object>, _optional_) — One `{code, field, message}` per image that could not be read (`code: "image_not_fetchable"`, `field: "images[i]"`). Such an image is not on the board and is still charged.

## Response example

```json
{
  "job_id": "5b3d2e22a29942faa12752c7f1e5673d",
  "lifecycle": "queued",
  "status_url": "http://v3-api.refabric.com/v1/jobs/5b3d2e22a29942faa12752c7f1e5673d",
  "result_url": "http://v3-api.refabric.com/v1/jobs/5b3d2e22a29942faa12752c7f1e5673d/result",
  "cancel_url": "http://v3-api.refabric.com/v1/jobs/5b3d2e22a29942faa12752c7f1e5673d/cancel"
}
```

## Result example

```json
{
  "job_id": "5b3d2e22a29942faa12752c7f1e5673d",
  "files": [
    {
      "file": "moodboard:5b3d2e22-a299-42fa-a127-52c7f1e5673d",
      "url": "https://files.example.com/moodboard.create/3.jpg",
      "media_type": "application/json",
      "task": "moodboard.create",
      "job_id": "5b3d2e22a29942faa12752c7f1e5673d",
      "created_at": "2026-09-30T10:02:11Z",
      "data": {
        "name": "SS27 resort",
        "description": "Sun-bleached neutrals and relaxed tailoring for a coastal resort season.",
        "status": "ready",
        "colours": [
          {
            "hex": "#E6D8C3",
            "name": "Sandshell",
            "pantone": "13-1106 TCX"
          }
        ],
        "items": [
          {
            "type": "fabric",
            "name": "washed linen",
            "url": "https://files.example.com/moodboard.create/1.jpg"
          },
          {
            "type": "print",
            "url": "https://files.example.com/moodboard.create/2.png"
          },
          {
            "type": "look",
            "url": "https://files.example.com/moodboard.create/3.jpg"
          }
        ],
        "keywords": [
          "resort",
          "relaxed tailoring",
          "linen"
        ]
      }
    }
  ],
  "has_more": false,
  "summary": {
    "requested": 10,
    "delivered": 9,
    "warnings": [
      {
        "code": "image_not_fetchable",
        "field": "images[4]",
        "message": "This image could not be read and is not on the board. It is still charged: the price is set by the number of images sent."
      }
    ]
  }
}
```

A `kind: trend` moodboard. The two items become the board's fabric and print sections, and the colours you send become the palette later tasks use; `data.colours` shows the palette read from the images.

## Built for

- Trend research from runway and street images
- A collection's direction, before design starts
- A brand's own look, from its lookbook (`kind: brand_dna`)

## What you get

- One `moodboard:` record — its name, description, status and keywords.
- The palette in `data.colours` and the fabrics, prints and key items found in the images in `data.items`.
- A cover image as the record's `url`, and a `summary` that counts the images used and names any that could not be read.

## Specs

- **Input formats:** Images, each one string: a URL, an upload (`file:…`) or a file from an earlier result (`art:…`). Upload types and sizes: `GET /v1/meta` `limits.upload`.
- **Input count:** `images`: 10 to 60; `items`: up to 120, each with its image
- **Output format:** One `moodboard:` record: its `data` is JSON in the record vocabulary, its `url` a preview image.
- **Outputs per job:** One record file, however many images you send.

## Choosing a task

- Use this when you want a direction read from images: what they share in colour, fabric, print and items. Use `brand_kit.create` when you want your own colours and images kept as they are, for generation to use.

## Errors

- `field_not_accepted`
- `field_not_supported`
- `invalid_option`
- `invalid_request`
- `moodboard_needs_images`
- `too_many_images`
- `too_many_items`
- `too_many_colours`
- `invalid_colour`
- `not_found`
- `permission_denied`
- `insufficient_credits`
- `images_not_fetchable`
- `no_brand_dna`
- `capacity_busy`
- `processing_failed`

## Related

- `image.generate` — It runs after: it takes this task's result.
- `range_plan.create` — It runs after: it takes this task's result.
- `brand_kit.create` — It does a neighbouring job.

## Good to know

- `images` holds at least 10 items.
- `images` holds at most 60 items.
- `items` holds at most 120 items.
- `items[].name` is at most 80 characters.
- `colours` holds at most 10 items.
- `items` is taken only with `kind: trend`.
- `colours` is taken only with `kind: trend`.

## For agents and code generation

- https://api.refabric.com/v1/tasks/moodboard.create/llms.txt
- https://api.refabric.com/v1/tasks/moodboard.create/openapi.json
- GET https://api.refabric.com/v1/tasks/moodboard.create
