# Append drawings

Add drawings to a pending chart analysis, keeping the card and the drawings already on it.

- Endpoint: `POST https://api.returning.ai/v1/analysis/{analysisId}/drawings`
- Section: Community / Chart analysis
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `sendMessage` (Shown in the dashboard as "Send Messages")
- Retries: A repeat returns 400; nothing is added twice
- Guide: hand-written
- Verified: code, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/chart-analysis/append-drawings

## When to use this

- Add a stop-loss or target line to an analysis after it was posted.
- Mark a new zone on the chart as the trade develops.
- Build up a card in steps, creating it with levels only and adding drawings afterwards.

**Instead:** Use [Update analysis](https://docs.returning.ai/api-reference/chart-analysis/update-analysis.md) instead to replace every drawing at once.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `sendMessage`

Use a Community API key with `sendMessage`, shown in the dashboard as Send Messages, and keep it on your server. Name the member who created the analysis in `sender`; only they can add drawings. Only send IDs from your own community's analyses. Personal API keys are not accepted here and return `401`.

## Behaviour

Only a `Pending` analysis accepts new drawings. They are added after the ones already on the card, which stay as they are; the levels and text don't change. Members with the channel open see the new drawings straight away. Nothing is posted in the channel, and no webhook fires.

Drawings use the same shapes as Create analysis: a type, your `clientObjectId`, points with a UTC `time` and a `price`, and an optional style.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `analysisId` | `string` | Yes | The analysis ID from Create analysis or List analyses. (24 hex characters) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `sendMessage`. (`Bearer <API_KEY>`) |
| `Content-Type` | `string` | Yes | Request body format. (`application/json`) |

### Body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `sender` | `string` | Yes | Username or email of the member who created the analysis. Required with a Community API key; any other member is refused. |
| `drawings` | `object[]` | Yes | The drawings to add. Each `clientObjectId` must be new, both within the request and on the card. (0-20 items per request) |
| `drawings.type` | `string` | Yes | Drawing type. `PARALLEL` needs exactly 3 points; `FIB_RETRACEMENT` needs 2 or 3. (`HORIZONTAL_LINE`, `LINE`, `RECTANGLE`, `PARALLEL` or `FIB_RETRACEMENT`) |
| `drawings.clientObjectId` | `string` | Yes | Your own name for the drawing, unique in the request. Use it to find the drawing again in the response. (Min 1 chars) |
| `drawings.points` | `object[]` | Yes | Chart points for the drawing. (3 for `PARALLEL`, 2-3 for `FIB_RETRACEMENT`) |
| `drawings.points.time` | `string \| integer` | Yes | Point time. (ISO 8601 date-time in UTC ending in `Z`, or epoch milliseconds) |
| `drawings.points.price` | `number` | Yes | Point price. |
| `drawings.style` | `object` | No | Optional colours and line style. |
| `drawings.style.color` | `string` | No | Line colour. (`#RRGGBB`) |
| `drawings.style.lineWidth` | `integer` | No | Line width. (1-10) |
| `drawings.style.lineStyle` | `string` | No | Line style. (`SOLID`, `DASHED` or `DOTTED`) |
| `drawings.style.fillColor` | `string` | No | Fill colour. An alpha channel such as `#00FF0033` is rejected. (`#RRGGBB`) |
| `drawings.style.borderColor` | `string` | No | Border colour. (`#RRGGBB`) |
| `drawings.style.opacity` | `number` | No | Opacity. (0-1) |
| `drawings.startHead` | `string` | No | `LINE` only: the start decoration. (`NONE`, `ARROW` or `EXTEND`; default `NONE`) |
| `drawings.endHead` | `string` | No | `LINE` only: the end decoration. (`NONE`, `ARROW` or `EXTEND`; default `NONE`) |
| `drawings.levels` | `number[]` | No | `FIB_RETRACEMENT` only: the ratios to draw. Omit for the standard set from -0.618 to 1.618. |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/analysis/66f000000000000000000b01/drawings \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "sender": "trader@example.com",
    "drawings": [
      {
        "type": "HORIZONTAL_LINE",
        "clientObjectId": "stop_line",
        "points": [
          {
            "time": "2026-09-27T08:00:00.000Z",
            "price": 1.075
          }
        ],
        "style": {
          "color": "#C62828"
        }
      }
    ]
  }'
```

## Response

A `200` returns the whole updated analysis in `data.analysis` and the number added in `data.addedCount`. There is no map of new IDs: find each new drawing in `shapes` by your `clientObjectId`.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `status` | `string` | always | Result of the request. (`success`) |
| `statusCode` | `integer` | always | The HTTP status, repeated. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The updated analysis and how many drawings were added. |
| `data.analysis` | `object` | always | The analysis after the change, with the same fields as `data.analysis` in Get analysis, without `message`. New drawings are at the end of `shapes`, each with your `clientObjectId` and its saved `id`. |
| `data.addedCount` | `integer` | always | Number of drawings added by this request. |

### Example response (200)

```json
{
  "status": "success",
  "statusCode": 200,
  "message": "Drawings appended successfully",
  "data": {
    "analysis": {
      "_id": "66f000000000000000000b01",
      "owner": {
        "id": "3247779"
      },
      "tempId": null,
      "serverId": "66f000000000000000000010",
      "topicId": "66f000000000000000000b11",
      "commentId": "66f000000000000000000b21",
      "timeframe": "H1",
      "users": {
        "up": [],
        "down": []
      },
      "shapes": [
        {
          "key": "analysis",
          "levels": {
            "pivot": {
              "price": 1.1,
              "clientObjectId": "pivot_main",
              "time": "2026-09-27T08:00:00.000Z"
            },
            "resistance": {
              "price": 1.12,
              "clientObjectId": "resistance_main",
              "time": "2026-09-27T08:00:00.000Z"
            },
            "support": {
              "price": 1.08,
              "clientObjectId": "support_main",
              "time": "2026-09-27T08:00:00.000Z"
            }
          },
          "metadata": {
            "direction": "BULLISH",
            "entryPlacement": "BELOW_CURRENT",
            "expiry": {
              "type": "HOURS",
              "hours": 24
            },
            "title": "EURUSD bounce from support",
            "preferenceText": "Holding above [[support_main:support]], targeting [[resistance_main:resistance]].",
            "timeframe": "H1",
            "symbol": "EURUSD"
          },
          "id": "66f000000000000000000b41",
          "isRaw": true,
          "timeRange": [1790496000000, 1790496312431]
        },
        {
          "type": "HORIZONTAL_LINE",
          "clientObjectId": "entry_line",
          "points": [
            {
              "time": "2026-09-27T08:00:00.000Z",
              "price": 1.095
            }
          ],
          "style": {
            "color": "#2E7D32"
          },
          "key": "h-ray",
          "id": "66f000000000000000000b31",
          "isRaw": true,
          "timeRange": [1790035200000, 1790956800000]
        },
        {
          "type": "HORIZONTAL_LINE",
          "clientObjectId": "stop_line",
          "points": [
            {
              "time": "2026-09-27T08:00:00.000Z",
              "price": 1.075
            }
          ],
          "style": {
            "color": "#C62828"
          },
          "key": "h-ray",
          "id": "66f000000000000000000b32",
          "isRaw": true,
          "timeRange": [1790035200000, 1790956800000]
        }
      ],
      "category": "analysis",
      "extra": {
        "happened": "[{\"type\":\"p\",\"children\":[{\"text\":\"Holding above \"},{\"type\":\"linked-text\",\"hoverIds\":[\"66f000000000000000000b41:0\"],\"children\":[{\"text\":\"support\",\"type\":\"text\",\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f61\"}],\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f71\"},{\"text\":\", targeting \"},{\"type\":\"linked-text\",\"hoverIds\":[\"66f000000000000000000b41:7\"],\"children\":[{\"text\":\"resistance\",\"type\":\"text\",\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f62\"}],\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f72\"},{\"text\":\".\"}],\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f60\"}]",
        "expected": "[{\"type\":\"p\",\"children\":[{\"text\":\"\"}],\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f80\"}]",
        "comments": "[{\"type\":\"p\",\"children\":[{\"text\":\"\"}],\"id\":\"7c1e4f9a-2b6d-4e0a-9f3b-1a2c3d4e5f80\"}]",
        "direction": "bullish",
        "currency_pair": "EURUSD",
        "horizon": "SHORT",
        "userId": 3247779,
        "level": [
          {
            "title": "1st Support",
            "value": 1.08
          },
          {
            "title": "2nd Support",
            "value": 0
          },
          {
            "title": "Intermediate Support",
            "value": 0
          },
          {
            "title": "Downside Confirmation",
            "value": 0
          },
          {
            "title": "Upside Confirmation",
            "value": 0
          },
          {
            "title": "Intermediate Resistance",
            "value": 0
          },
          {
            "title": "2nd Resistance",
            "value": 0
          },
          {
            "title": "1st Resistance",
            "value": 1.12
          },
          {
            "title": "Pivot",
            "value": 1.1
          }
        ],
        "timeframe": "H1",
        "analysisType": {
          "expiry": 1,
          "title": "EURUSD bounce from support",
          "type": "bullish_bounce"
        },
        "status": "Pending",
        "intermediate": false
      },
      "roles": [],
      "private": false,
      "timeRange": {
        "x": [1790035200000, 1790956800000],
        "y": [1.078, 1.122]
      },
      "snapshot": null,
      "translationSource": "en",
      "translations": {},
      "createdAt": "2026-09-27T08:05:12.431Z",
      "updatedAt": "2026-09-27T09:20:00.000Z",
      "__v": 0
    },
    "addedCount": 1
  }
}
```

## Errors

These errors have no machine-readable code; branch on the HTTP status and read `message`. A `sender` who isn't the creator gets `500`, not `403`. The `401` for a missing or unknown key has `detail` and a `meta` object; the `401` for a missing permission has only `message`.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | Nothing was added. Read `message`: - `Append Drawings Request Validation validation error.`: a drawing breaks its rule, such as an unknown `type`, a colour that isn't `#RRGGBB`, the wrong number of points, or two drawings with the same `clientObjectId`. `detail` points to the drawing. - `clientObjectId must be unique. Duplicate IDs found: ...`: the card already has a drawing with this `clientObjectId`. If you are retrying, the earlier request already added it; otherwise choose a new name. - `Invalid analysis ID format`: send the 24-character analysis ID. - `Sender field is required`: send `sender`. - `The Analsys is not possible to update as status changed!` (spelled this way): the analysis is no longer `Pending`. |
| 401 | - | The key is missing (`Token is require.`) or unknown (`Token is wrong.`), it's a personal key, or it lacks `sendMessage` (`Your API key does not have permission to access this action`). Send `Authorization: Bearer <API_KEY>` with a Community API key, and add Send Messages in Settings > Integration > API Keys. |
| 500 | - | Read the body: - `The analysis was not created by the user`: `sender` isn't the member who created the analysis. Send the creator's username or email; nothing was added. - `Failed to append drawings`: the change failed. Read the analysis with Get analysis, then send only the drawings that are missing. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | `Analysis not found`: no analysis has this ID, or it was deleted. `User not found`: no member of your community has the `sender` username or email. |

**Retries:** This endpoint doesn't accept an `Idempotency-Key`, but drawings can't be added twice: if a request that already succeeded is sent again with the same `clientObjectId` values, it returns `400` with `Duplicate IDs found` and adds nothing. After a timeout, you can resend the same body, and treat that `400` as done. After a `500 Failed to append drawings`, read the analysis back first.

## Next step

- [Check the drawings with Get analysis](https://docs.returning.ai/api-reference/chart-analysis/get-analysis.md): `GET /v1/analysis/{analysisId}`. Read the analysis back and find each new drawing by its `clientObjectId`.
- [Update analysis](https://docs.returning.ai/api-reference/chart-analysis/update-analysis.md): `PATCH /v1/analysis/{analysisId}`. Change the title, scenarios or expiry of the same analysis.
