# Colors

A result's colors: read them, recolor it, keep your brand's palettes.

### The result's paint map

`GET /v1/conversions/{id}/paints`

Every tier, like the preview: the paints the file draws with its ground left out, in index order, each with its share of the frame, the ground, and a link to `paints.bin`: zlib of an index plane then a weight plane (width × height × 4 bytes each: every pixel's four largest paints, 255 = none, and their weights × 255). A pixel of any recolor is Σ w·c′ (premultiplied), with the ground under it as (1 − Σ w)·ground. Made with the conversion (an older result's on the first ask); a drawing of more than 24 paints has none.

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |

```bash
curl https://api.vecsnap.com/v1/conversions/00000000-0000-4000-8000-000000000000/paints \
  -H "Authorization: Bearer $VECSNAP_API_KEY"
```

Answer 200:

```json
{
  "width": 1,
  "height": 1,
  "ground": "#1f6feb",
  "groundArea": 0,
  "paints": [
    {
      "key": "#1f6feb",
      "area": 0
    }
  ],
  "map": "https://files.vecsnap.com/…",
  "expiresIn": 1
}
```

Answer 404:

```json
{
  "error": {
    "code": "not_found",
    "message": "There is nothing at that id for this caller.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 409:

```json
{
  "error": {
    "code": "conversion_failed",
    "message": "The conversion ended without a result.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 429:

```json
{
  "error": {
    "code": "compute_limit_reached",
    "message": "Today's editing time for your account (or your guest address) is used: nothing was made.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `not_found` | Check the id. Another account's objects read as not found too. |
| 409 | `conversion_failed` | Convert the image again. Converting is free. |
| 409 | `not_in_test_mode` | Use a live key for it, or test the conversion and its SVG files. |
| 409 | `not_ready` | Wait for it to finish (a webhook, the events stream, or a read), then ask again. |
| 409 | `result_outdated` | Convert the image again. |
| 409 | `too_many_colors` | Redraw it with fewer colors first. |
| 429 | `compute_limit_reached` | Come back after Retry-After (UTC midnight). |
| 429 | `derive_busy` | Nothing was done. Send it again in a moment (Retry-After). |
| 429 | `editing_capacity_reached` | Come back after Retry-After (UTC midnight), or take a plan: a customer's editing is its own. |
| 502 | `derive_failed` | Try again. The coarser picture still shows. |

Every other refusal it may answer: `invalid_id` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### Preview fewer colors

`GET /v1/conversions/{id}/palette`

Previews of a redraw with fewer colors. Without `count`: the WHOLE rail in one answer (`counts`), every count in the engine's bounds, colors only: what a client asks once, then reads. With `count`: the colors the engine REACHES for at most that many (from the result's kept original; it can be fewer), and the result's own colors MERGED into them (`mapping`), a recolor of the very drawing a client draws exactly. With `format` (a web file with a picture), also `picture`: the merged drawing at the preview's size; its deeper tiles are POST /conversions/{id}/tiles with `palette`. The redraw itself (POST /images/{id}/conversions with `options.maxColors`) traces new shapes.

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |
| `count` | query | integer, 2 to 24 | no | One count; left out, the whole rail. |
| `format` | query | string | no | With `count`, a web file's id: its merged picture too. |

```bash
curl https://api.vecsnap.com/v1/conversions/00000000-0000-4000-8000-000000000000/palette \
  -H "Authorization: Bearer $VECSNAP_API_KEY"
```

Answer 200:

```json
{
  "counts": [
    {
      "asked": 0,
      "count": 1,
      "colors": [
        "#1f6feb"
      ],
      "mapping": {}
    }
  ]
}
```

Answer 404:

```json
{
  "error": {
    "code": "not_found",
    "message": "There is nothing at that id for this caller.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 409:

```json
{
  "error": {
    "code": "conversion_failed",
    "message": "The conversion ended without a result.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 429:

```json
{
  "error": {
    "code": "compute_limit_reached",
    "message": "Today's editing time for your account (or your guest address) is used: nothing was made.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `not_found` | Check the id. Another account's objects read as not found too. |
| 409 | `conversion_failed` | Convert the image again. Converting is free. |
| 409 | `not_in_test_mode` | Use a live key for it, or test the conversion and its SVG files. |
| 409 | `not_ready` | Wait for it to finish (a webhook, the events stream, or a read), then ask again. |
| 409 | `original_not_kept` | Upload the file again. |
| 409 | `result_outdated` | Convert the image again. |
| 429 | `compute_limit_reached` | Come back after Retry-After (UTC midnight). |
| 429 | `derive_busy` | Nothing was done. Send it again in a moment (Retry-After). |
| 429 | `editing_capacity_reached` | Come back after Retry-After (UTC midnight), or take a plan: a customer's editing is its own. |
| 502 | `derive_failed` | Try again. The coarser picture still shows. |

Every other refusal it may answer: `format_unavailable` (400), `invalid_id` (400), `invalid_options` (400), `invalid_request` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### List a result's recolors

`GET /v1/conversions/{id}/variants`

The result's recolors, the last used first: the 10 used last, in one page (`hasMore` says older ones exist; they are not listed, and saving one again brings it first).

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |

```bash
curl https://api.vecsnap.com/v1/conversions/00000000-0000-4000-8000-000000000000/variants \
  -H "Authorization: Bearer $VECSNAP_API_KEY"
```

Answer 200:

```json
{
  "object": "list",
  "data": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "edits": [
        {
          "op": "example",
          "shape": 0
        }
      ],
      "colors": {},
      "usedAt": "2026-10-24T12:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

Answer 404:

```json
{
  "error": {
    "code": "not_found",
    "message": "There is nothing at that id for this caller.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `not_found` | Check the id. Another account's objects read as not found too. |

Every other refusal it may answer: `invalid_id` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### Change a result: recolor it, edit its shapes

`POST /v1/conversions/{id}/variants`

A REVISION of the result. `colors` maps the file's own colors to new ones, every use of each (fills, strokes, gradient stops, the ground); two to one value merge them. Keys are the result's paints and its ground (`/facts`). `edits` (at most 1000; at most 25 of them slow to replay: simplify, merge, subtract, intersect, exclude, offset, outline, fillet) change its shapes, applied in order before the colors: hide, duplicate, place, simplify, round, point, corner, add_point, delete_point, fillet, merge, arrange, paint, subtract, intersect, exclude, flip, offset, outline, stretch, align. Each addresses a shape by its index in the table with the background kept; the background can only be hidden, and is no edit's other shape (into, by, with). It is stored canonical, and the same one saved again is the same variant, used now. Free; its files download under the image's unlock (`/download?variant=`). An edit only the engine draws, the file's own geometry (simplify, round, point, corner, add_point, delete_point, fillet, merge, subtract, intersect, exclude, offset, outline), is added once the image is unlocked (402 `unlock_required` before, nothing saved); one a saved revision of the result already holds is kept.

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |
| `colors` | body | object | no | `{"#old": "#new"}`, each `#rrggbb`. |
| `edits` | body | object | no | Shape edits, in order: a list of at most 1000 `{op, shape, …}`, at most 25 of them slow (simplify, merge, subtract, intersect, exclude, offset, outline, fillet). |

```bash
curl -X POST https://api.vecsnap.com/v1/conversions/00000000-0000-4000-8000-000000000000/variants \
  -H "Authorization: Bearer $VECSNAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"colors":{},"edits":null}'
```

Answer 200:

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "edits": [
    {
      "op": "example",
      "shape": 0
    }
  ],
  "colors": {},
  "usedAt": "2026-10-24T12:00:00.000Z"
}
```

Answer 402:

```json
{
  "error": {
    "code": "unlock_required",
    "message": "This image is not unlocked yet: its files, its points and its reshaping edits wait for it.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 404:

```json
{
  "error": {
    "code": "not_found",
    "message": "There is nothing at that id for this caller.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

Answer 409:

```json
{
  "error": {
    "code": "conversion_failed",
    "message": "The conversion ended without a result.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 402 | `unlock_required` | Unlock it (1 credit, once per image), then send the same request again. |
| 404 | `not_found` | Check the id. Another account's objects read as not found too. |
| 409 | `conversion_failed` | Convert the image again. Converting is free. |
| 409 | `not_in_test_mode` | Use a live key for it, or test the conversion and its SVG files. |
| 409 | `not_ready` | Wait for it to finish (a webhook, the events stream, or a read), then ask again. |
| 409 | `result_outdated` | Convert the image again. |
| 429 | `compute_limit_reached` | Come back after Retry-After (UTC midnight). |
| 429 | `derive_busy` | Nothing was done. Send it again in a moment (Retry-After). |
| 429 | `editing_capacity_reached` | Come back after Retry-After (UTC midnight), or take a plan: a customer's editing is its own. |
| 502 | `derive_failed` | Try again. The coarser picture still shows. |

Every other refusal it may answer: `invalid_colors` (400), `invalid_edits` (400), `invalid_id` (400), `invalid_request` (400), `malformed_body` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### List palettes

`GET /v1/palettes`

The account's palettes, the last changed first: at most 20, in one page.

```bash
curl https://api.vecsnap.com/v1/palettes \
  -H "Authorization: Bearer $VECSNAP_API_KEY"
```

Answer 200:

```json
{
  "object": "list",
  "data": [
    {
      "object": "palette",
      "id": "00000000-0000-4000-8000-000000000000",
      "name": "Brand colors",
      "colors": [
        "#1f6feb"
      ],
      "updatedAt": "2026-10-24T12:00:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

Every refusal it may answer: `invalid_api_key` (401), `unauthenticated` (401), `account_required` (403), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### Save a palette

`POST /v1/palettes`

A new palette; an account keeps at most 20 (409 `palette_limit`).

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `name` | body | string | yes | Trimmed. |
| `colors` | body | array of string | yes | `#rrggbb`, either case; a repeated one is kept once. |

```bash
curl -X POST https://api.vecsnap.com/v1/palettes \
  -H "Authorization: Bearer $VECSNAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Brand colors","colors":["example"]}'
```

Answer 200:

```json
{
  "object": "palette",
  "id": "00000000-0000-4000-8000-000000000000",
  "name": "Brand colors",
  "colors": [
    "#1f6feb"
  ],
  "updatedAt": "2026-10-24T12:00:00.000Z"
}
```

Answer 409:

```json
{
  "error": {
    "code": "palette_limit",
    "message": "The account has the most palettes it can keep.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 409 | `palette_limit` | Delete a palette, then save the new one. |

Every other refusal it may answer: `invalid_palette` (400), `invalid_request` (400), `malformed_body` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_required` (403), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### Change a palette

`PATCH /v1/palettes/{id}`

Its name and colors, whole. Another's is 404 `palette_not_found`, the same as none.

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |
| `name` | body | string | yes | Trimmed. |
| `colors` | body | array of string | yes | `#rrggbb`, either case; a repeated one is kept once. |

```bash
curl -X PATCH https://api.vecsnap.com/v1/palettes/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $VECSNAP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Brand colors","colors":["example"]}'
```

Answer 200:

```json
{
  "object": "palette",
  "id": "00000000-0000-4000-8000-000000000000",
  "name": "Brand colors",
  "colors": [
    "#1f6feb"
  ],
  "updatedAt": "2026-10-24T12:00:00.000Z"
}
```

Answer 404:

```json
{
  "error": {
    "code": "palette_not_found",
    "message": "There is no such palette in this account.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `palette_not_found` | Use a palette id GET /palettes listed. |

Every other refusal it may answer: `invalid_id` (400), `invalid_palette` (400), `invalid_request` (400), `malformed_body` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_required` (403), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

### Delete a palette

`DELETE /v1/palettes/{id}`

Removed for good. Another's is 404 `palette_not_found`, the same as none.

| Field | In | Type | Required | What |
| --- | --- | --- | --- | --- |
| `id` | path | id | yes |  |

```bash
curl -X DELETE https://api.vecsnap.com/v1/palettes/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer $VECSNAP_API_KEY"
```

Answer 200:

```json
{
  "id": "00000000-0000-4000-8000-000000000000",
  "deleted": true
}
```

Answer 404:

```json
{
  "error": {
    "code": "palette_not_found",
    "message": "There is no such palette in this account.",
    "requestId": "req_8Gq2Lw0fTz1kVb6s"
  }
}
```

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `palette_not_found` | Use a palette id GET /palettes listed. |

Every other refusal it may answer: `invalid_id` (400), `invalid_api_key` (401), `unauthenticated` (401), `account_required` (403), `account_stopped` (403), `email_unverified` (403), `plan_required` (403), `account_exists` (409), `email_refused` (422), `rate_limited` (429), `internal_error` (500), `auth_unavailable` (503).

---
This page: https://vecsnap.com/docs/api/colors
