# The Storrito API

The Storrito API allows to import images and videos that Storrito will
  post as Instagram Stories, Instagram Reels or TikTok posts. Furthermore it is possible to define Instagram Stickers that will be added to the story image or video.

The API is designed as 'HTTP API', meaning you can use `curl` and other HTTP
clients to interact with the API and it will return common HTTP status codes.
But the API is neither RESTful nor a full-blown remote-procedure-call
service (like gRPC). The focus of the API design is to provide a good developer
experience. Nowadays the best option might be to follow the OpenAPI
specification, which allows to generate client SDKs for the most common
programming languages. However, the downside is that OpenAPI and maintaining
dozens of client SDKs is quite complex. For the moment we focus on providing a
simple 'HTTP API' that is easy to use.

The API offers remote-procedure-calls (RPC) via HTTP. The arguments for the procedure
are send as JSON via a HTTP POST request. All API endpoints starts with:

    https://ORG_UUID.storrito.com/api/v1/

followed by the name of the procedure. Usually the response will also contain
JSON data. The top-level data structure of the request and the response is
always a map. This allows us to add more map entries without [breaking](https://www.youtube.com/watch?v=oyLBGkS5ICk) your code.

No API or website has 100% availability, therefore be prepared to handle the
following HTTP status codes and retry the HTTP request:

- [HTTP 429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) 'Too
   Many Requests': the API will return this status code to signal that you run
   into a rate limit. The default quota allows 60 requests per minute (using a
   token bucket algorithm). If the procedure has a stricter quota this will be
   mentioned in its documentation. A rate limit can also happen, when the API
   servers are too busy to accept further requests.

- The API runs behind a HTTPs load balancer. When we deploy an update for the
  API the load balancer may return a [HTTP 502](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502), a [HTTP  503](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) or a [HTTP  504](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504).

Retry the HTTP request either with an [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) or just retry it
every 2 seconds plus some random milliseconds (between 0-999ms).

### Recommended `rpc` helper (Node.js)

Use this helper function for all API calls. It automatically retries on rate-limit (429) and transient server errors (502, 503, 504):

```js
async function rpc(procedure, params = {}) {
  const maxAttempts = 5;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fetch(`${BASE_URL}/${procedure}`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(params),
    });
    if ([429, 502, 503, 504].includes(res.status)) {
      if (attempt === maxAttempts) {
        throw new Error(`${procedure} failed after ${maxAttempts} attempts (${res.status})`);
      }
      const delay = 2000 + Math.random() * 1000;
      console.log(`${procedure}: HTTP ${res.status}, retrying in ${Math.round(delay)}ms...`);
      await new Promise((r) => setTimeout(r, delay));
      continue;
    }
    if (!res.ok) {
      const text = await res.text();
      throw new Error(`${procedure} failed (${res.status}): ${text}`);
    }
    return res.json();
  }
}
```

## Story Components

Web components for building Instagram Story-sized media with HTML. See the [Story Components](/documentation/api/v1/story-components) documentation.

## Use with AI Coding Agents

The Storrito API works great with AI coding agents like Claude Code, Cursor, GitHub Copilot, and others. You can paste the following example prompt into your agent to get started quickly:

```
Write a Node.js script that posts an image to Instagram Stories using the
Storrito API. Use the first connected Instagram account. Add a hashtag
sticker with "#travel" and a link sticker pointing to "https://example.com".

API docs: https://ORG_UUID.storrito.com/documentation/api/v1/index.md
Story component docs: https://ORG_UUID.storrito.com/documentation/api/v1/story-components.md

Base URL: https://ORG_UUID.storrito.com/api/v1/
Ask me for the bearer token before writing any code.
```

This is just a starting point. Customize the prompt to match your use case, for example by changing the sticker types, scheduling the post for a specific time, or using a video instead of an image.

## Authentication

The API uses Bearer token authentication. You can create API credentials in your
account settings under [API Credentials](/ui/api-credential/manage/).

When you create an API credential, a Bearer token is shown once. The server only
stores a hash, so the token cannot be retrieved again.

Include the token in the `Authorization` header of every request:

    Authorization: Bearer YOUR_BEARER_TOKEN

Store your token in a shell variable so you can copy-paste the `curl` examples below:

```
TOKEN="your-bearer-token-here"
```

Example using `curl`:

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/PROCEDURE_NAME \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

This API is meant for server-to-server communication, please do not include
Bearer tokens in client-side code.


## Validation

Each procedure call has a JSON schema that is used to validate the input
parameters. An example:

The request:

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/status-instagram-story \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storyPostUud": "550e8400-e29b-41d4-a716-446655440000", "blah": true}'
```

Will result in a HTTP 400 response with the body:
```json
{
  "errorMessage" : "invalid params for procedure: status-instagram-story",
  "procedureName" : "status-instagram-story",
  "paramsJsonSchema" : {
    "type" : "object",
    "properties" : {
      "storyPostUuid" : {
        "type" : "string",
        "format" : "uuid"
      }
    },
    "required" : [ "storyPostUuid" ],
    "additionalProperties" : false
  },
  "validationErrorExplanation" : {
    "storyPostUud" : [ "should be spelled :storyPostUuid" ],
    "blah" : [ "disallowed key" ]
  }
}
```

Besides an error message (`errorMessage`) the server will also show you the JSON
schema (`paramsJsonSchema`) and explain why the parameters for the procedure are
invalid (`validationErrorExplanation`). In the example above there is a typo in `storyPostUud` and the key `blah` is not defined for the procedure `status-instagram-story`.

## Webhooks

Webhooks let Storrito notify your tools as soon as an Instagram Story post
changes status. This is useful for no-code automations in Make, Zapier, n8n,
Notion workflows, or your own backend because you do not need to poll
`status-instagram-story` repeatedly for API-created posts.

Configure webhook URLs in the Storrito app under **Storrito API → Webhooks**.
Each organization can configure up to **5 webhook URLs**. Webhook URLs must use
HTTPS.

### Delivery behavior

Storrito sends an HTTP `POST` request with a JSON body. Your endpoint should
return any `2xx` HTTP status code to mark the delivery as successful. If the
endpoint is unavailable or returns a non-2xx status code, Storrito retries the
delivery with backoff and eventually marks it as failed.

For v1, webhook deliveries are intentionally simple and are not signed.
Treat webhook payloads as notifications and call the API if your automation needs
to verify the latest state.

### Headers

```text
Content-Type: application/json
X-Storrito-Event: instagram_story_post.status_changed
X-Storrito-Delivery: DELIVERY_UUID
```

### Event: `instagram_story_post.status_changed`

This event is sent when an Instagram Story post changes status.

Possible status values:

- `scheduled` — the story post was scheduled.
- `executed` — the story was posted to Instagram.
- `failed` — posting failed. The payload includes `errorMessage`.
- `canceled` — the story post was canceled before it was posted.

Example payload:

```json
{
  "event": "instagram_story_post.status_changed",
  "storyPostUuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "executed",
  "occurredAt": "2026-06-09T12:34:56Z"
}
```

Failed payload example:

```json
{
  "event": "instagram_story_post.status_changed",
  "storyPostUuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "errorMessage": "Instagram returned an error.",
  "occurredAt": "2026-06-09T12:34:56Z"
}
```

## Errors

All 'expected' errors will return a [HTTP 400](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) status code.
Unexpected errors are caused by bugs or downtimes on the server-side, they will
return a [HTTP 500](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) status code.
These errors should only be retried, if the procedure
is [idempotent](https://en.wikipedia.org/wiki/Idempotence).

Using standard HTTP status codes is the reason why we call the design an 'HTTP
API' and one of the reason we do not
use [JSON-RPC](https://en.wikipedia.org/wiki/JSON-RPC).

## Dates

All dates in the API use [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format in UTC (e.g. `"2025-06-15T14:30:00Z"`). Most languages have built-in support for parsing and formatting ISO 8601 date-time strings.

## Instagram Limits and Best Practices

Instagram enforces its own limits that are outside of Storrito's control. Violating them can result in temporary posting blocks or automation warnings on your account.

### Posting frequency

- Instagram allows roughly **15–25 stories per account per day**. Exceeding this may trigger a temporary cooldown or a "try again later" error.
- Space out your posts. Avoid bursting many stories for the same account within a few minutes.
- The Storrito API enforces its own per-account quota (100 story posts per account per 24 hours) as a safety net, but Instagram's limits are lower and less predictable.

### Content uniqueness

- **Do not post the same story design repeatedly.** Instagram's systems detect duplicate content and may flag your account for spam or automation.
- Each story should have a unique visual — change the background image or video, sticker text, or layout between posts.
- If you are testing your integration, preview your story locally using the `<insta-story>` web components in a browser instead of posting to Instagram repeatedly.

### Account health

- If Instagram returns an error or blocks a post, back off and wait before retrying. Do not retry failed posts in a tight loop.

## Video Requirements

When you upload a video for an Instagram Story, the API validates it **before** rendering. Videos that do not meet the following requirements are rejected with an HTTP 400 error and a descriptive message.

- **Codec**: H.264 (`h264`)
- **Resolution**: 1080 x 1920 pixels (9:16 portrait)
- **Duration**: 0.5 – 60 seconds
- **Bitrate**: ≤ 30 Mbps

### Converting a video with ffmpeg

Use the following `ffmpeg` command to convert any video to the required format:

```bash
ffmpeg -i input.mov \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
  -c:v libx264 -crf 18 -r 30 \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  -t 60 \
  output.mp4
```

What this does:

- **`scale=1080:1920:...,pad=1080:1920:...`** — scales the video to fit within 1080×1920 while keeping the aspect ratio, then pads with black bars to exactly 1080×1920.
- **`-c:v libx264 -crf 18`** — encodes with H.264 at high quality. Increase the CRF value (e.g. 23) for a smaller file size.
- **`-r 30`** — sets 30 fps.
- **`-c:a aac -b:a 128k`** — encodes audio as AAC at 128 kbps.
- **`-movflags +faststart`** — moves the MP4 metadata to the beginning for faster HTTP streaming.
- **`-t 60`** — caps the video at 60 seconds.

After conversion, verify the result with:

```bash
ffprobe -v quiet -print_format json -show_streams output.mp4
```

Check that the video stream shows `"codec_name": "h264"`, `"width": 1080`, `"height": 1920`, and `"bit_rate"` below `30000000`.

## The API procedures

Please find below the procedures that are offered to automate your Storrito
account:

### cancel-instagram-reel

Cancels a scheduled Instagram reel before it is posted.

Provide the `reelPostUuid` that was returned by the `schedule-instagram-reel` procedure.

- If the reel is still **scheduled**, it will be canceled and the response status will be `canceled`.
- If the reel was **already canceled**, the response status will be `canceled` (idempotent).
- If posting **already started**, **succeeded**, or **failed**, an error is returned because the reel cannot be canceled anymore.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/cancel-instagram-reel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reelPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "reelPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID that identifies a scheduled Instagram reel."
    }
  },
  "required" : [ "reelPostUuid" ]
}
```

**Example Output:**
```json
{
  "reelPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "canceled"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this reel."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "canceled" ],
      "description" : "The status after the cancel attempt: \"canceled\" when successfully canceled or already canceled."
    }
  },
  "required" : [ "reelPostUuid", "status" ]
}
```


### cancel-instagram-story

Cancels a scheduled Instagram story post before it is posted.

Provide the `storyPostUuid` that was returned by the `schedule-instagram-story` procedure.

- If the story is still **scheduled**, it will be canceled and the response status will be `canceled`.
- If the story was **already canceled**, the response status will be `canceled` (idempotent).
- If the story was **already executed** or **failed**, an error is returned because it cannot be canceled.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/cancel-instagram-story \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storyPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "storyPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID returned by `schedule-instagram-story`. Identifies which story post to cancel."
    }
  },
  "required" : [ "storyPostUuid" ]
}
```

**Example Output:**
```json
{
  "storyPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "canceled"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this story post."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "canceled", "executed", "failed" ],
      "description" : "The status after the cancel attempt: \"canceled\" (successfully canceled or already canceled), \"executed\" (already posted, cannot cancel), or \"failed\" (already posted but failed, cannot cancel)."
    }
  },
  "required" : [ "storyPostUuid", "status" ]
}
```


### cancel-tiktok-post

Cancels a scheduled TikTok post before it is posted.

Provide the `tiktokPostUuid` that was returned by the `schedule-tiktok-post` procedure.

- If the post is still **scheduled** or **in progress**, it will be canceled and the response status will be `canceled`.
- If the post was **already canceled**, the response status will be `canceled` (idempotent).
- If the post was **already executed** or **failed**, an error is returned because it cannot be canceled.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/cancel-tiktok-post \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tiktokPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "tiktokPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID that identifies a TikTok post."
    }
  },
  "required" : [ "tiktokPostUuid" ]
}
```

**Example Output:**
```json
{
  "tiktokPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "canceled"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this TikTok post."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "canceled" ],
      "description" : "The status after the cancel attempt: \"canceled\" when successfully canceled or already canceled."
    }
  },
  "required" : [ "tiktokPostUuid", "status" ]
}
```


### create-connect-link

Creates a connect-link: a shareable URL that lets an Instagram account owner connect their account to your Storrito workspace in the browser.

Share the returned `connectLinkUrl` with the person who owns the Instagram account (for example your customer). They open it, enter their Instagram credentials on the Storrito connect page, and complete any Instagram verification steps there. Their credentials never pass through your integration.

Once the account is connected it appears in `list-instagram-users` and can be used with `schedule-instagram-story`.

An organization can have at most 100 connect-links. Delete unused links in the Storrito app.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/create-connect-link \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"connectLinkUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5", "description": "Customer: ACME Corp"}'
```

**Example Input:**
```json
{
  "connectLinkUuid" : "4b52922a-3341-4580-adc2-edb475d712c2",
  "description" : "Vl7CQEx0cm1Nf96l15MwV761on8u3670FJzFb6PflYFsqb4LLxKvnp4M79vywviK0y179Z92TQ9u51C4v9YgWMQ7Z2BJuT498rY51bbTt7Tv8tBS7fdMu5rN5Rzji72HB6ghP3647feWl4592MUDwQS"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "connectLinkUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this connect-link. Ensures idempotency — creating a connect-link with the same UUID again returns the same URL instead of creating a duplicate. Use `generate-uuid` to create one."
    },
    "description" : {
      "type" : "string",
      "maxLength" : 500,
      "description" : "Optional note about who this link is for (e.g. the customer name). Shown in the connect-link list in the Storrito app. Maximum 500 characters."
    }
  },
  "required" : [ "connectLinkUuid" ]
}
```

**Example Output:**
```json
{
  "connectLinkUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "connectLinkUrl" : "mWGpAJgu7SdK77XqbGR52q8FEX6r91"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "connectLinkUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this connect-link."
    },
    "connectLinkUrl" : {
      "type" : "string",
      "description" : "The shareable connect URL. The Instagram account owner opens it in a browser and connects their account to this Storrito workspace. No Storrito login is required — the URL itself is the authorization, so share it only with the intended person."
    }
  },
  "required" : [ "connectLinkUuid", "connectLinkUrl" ]
}
```


### create-draft

Renders a page built with `<insta-story>` web components and puts it into the Storrito gallery as a draft, without scheduling it.

   Use it when a person should approve the design first: the draft shows up in the gallery of the Storrito app, where it can be opened in the editor, changed, and scheduled by hand as an Instagram story, an Instagram reel or a TikTok post. Nothing is posted by this procedure.

   Provide either the `url` of your hosted page **or** pass the HTML directly via the `html` parameter. Exactly one of `url` or `html` must be provided. The HTML format, the sticker components, the layout advice and the sandboxing are the same as for `schedule-instagram-story`. A video may be up to 60 seconds long.

   Optionally provide a `name` that the gallery shows for the draft, and a `folderName` to put the draft into a top-level gallery folder (created when it does not exist yet), so that all drafts of your integration end up in one place.

   Unknown JSON keys are rejected. This prevents typos from being silently ignored.

   Every request requires a `draftUuid` that you generate on your side. This ensures idempotency — if a draft with that UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests.

   The response contains an `editorUrl` that opens the draft in the Storrito editor. Send it to the person who reviews the draft; it requires a login to the Storrito workspace.

   **Preview:** Rendering starts right after the draft was created. As soon as it has finished (usually within a minute), `status-draft` returns a `previewUrl`: a JPEG of the rendered design with background, text and stickers.

   **Warnings:** Unknown `<insta-*>` elements and unknown attributes on sticker elements are ignored, but reported in the `warnings` array of the response, and so are stickers that overlap each other. The draft is created anyway. If a warning names something you meant to use, delete the draft with `delete-draft` and create it again with the corrected HTML.

   **Mentions:** Resolving an `<insta-mention>` username uses one of your connected Instagram accounts. If all of them are busy with another request at that moment, the request fails with HTTP 429. Retry it later with the same `draftUuid`.

   **Quota:** A maximum of 100 drafts can be created in any rolling 24-hour window. Deleted drafts count as well.

   **Request duration:** Before the request returns, the server loads your page in a headless browser, extracts the sticker data and uploads the media. This can take longer than 30 seconds, so configure a client read timeout of at least 120 seconds. If your client times out anyway, retry with the same `draftUuid`.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/create-draft \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "html": "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag></div></insta-story>",
       "draftUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5",
       "name": "Travel promo",
       "folderName": "API drafts"
     }'
   ```

**Example Input:**
```json
{
  "html" : "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag></div></insta-story>",
  "draftUuid" : "3dd3f017-13cc-4dac-a2a0-876583180dd5",
  "name" : "Travel promo",
  "folderName" : "API drafts"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "url" : {
      "type" : "string",
      "description" : "The URL of your page built with <insta-story> web components. The server will load this URL, render the design, and extract any sticker metadata. Provide either `url` or `html`, but not both."
    },
    "html" : {
      "type" : "string",
      "maxLength" : 10485760,
      "description" : "Inline HTML string containing your <insta-story> web components. The server injects the component JS and fonts automatically — just provide the HTML. Provide either `url` or `html`, but not both. Maximum 10 MB. Media uploaded via `create-temp-blob-upload-url` can be referenced by its temp-blob `url` in src attributes."
    },
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this draft. Ensures idempotency — if a draft with this UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests. Use `generate-uuid` to create one."
    },
    "name" : {
      "type" : "string",
      "minLength" : 1,
      "maxLength" : 200,
      "description" : "Optional name of the draft, shown in the Storrito gallery so that the reviewer can tell drafts apart. Defaults to \"api-story\". Maximum 200 characters."
    },
    "folderName" : {
      "type" : "string",
      "minLength" : 1,
      "maxLength" : 512,
      "description" : "Optional name of a top-level gallery folder to put the draft into, for example \"API drafts\". The folder is created when it does not exist yet. When omitted the draft is put on the top-level of the gallery."
    }
  },
  "required" : [ "draftUuid" ]
}
```

**Example Output:**
```json
{
  "draftUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "editorUrl" : "mWGpAJgu7SdK77XqbGR52q8FEX6r91",
  "warnings" : [ "9u21pO9PGXutn1uyr5i", "7ZgTGOQ4trbm0v0jmZ0TaO9fC1a", "S", "qVh4clHvC7DeFdw1", "lLNL4NN97o5J9O9H5NVD", "z73q75tZeZ923", "4A4PK5Yqq6u2c400BGv72inv1b9ut1", "xIktpNM", "wkoiYQbByQI7k7HPnpkW7v9w5dd" ]
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this draft. Pass it to `status-draft` to poll for the rendered preview, or to `delete-draft` to delete the draft."
    },
    "editorUrl" : {
      "type" : "string",
      "description" : "URL that opens the draft in the Storrito editor. Share it with the person who reviews the draft; it requires a login to the Storrito workspace."
    },
    "warnings" : {
      "type" : "array",
      "items" : {
        "type" : "string"
      },
      "description" : "Only present when the HTML contained unknown `<insta-*>` elements or unknown attributes on sticker elements (they were ignored), or when two stickers overlap on the canvas. The draft was created anyway. Check every warning: if it names something you meant to use or a layout you did not intend, delete the draft with `delete-draft` and create it again with the corrected HTML."
    }
  },
  "required" : [ "draftUuid", "editorUrl" ]
}
```


### create-temp-blob-upload-url

Creates a presigned upload URL for a temporary media blob (image, video or audio, max 100 MB), for media that only exists as a local file — media that already has a public URL can be referenced in the story `html` directly and needs no temp-blob. PUT the file to the returned `uploadUrl` — the bytes go directly to storage, no base64 anywhere:

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/create-temp-blob-upload-url \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tempBlobUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5", "contentType": "video/mp4", "contentLengthBytes": '$(stat -c%s story.mp4)'}'

curl -T story.mp4 -H "Content-Type: video/mp4" "UPLOAD_URL"
```

Afterwards use the stable `url` from the result as the src inside the `html` of `schedule-instagram-story`, `schedule-instagram-reel` or `schedule-tiktok-post`, and to preview the media in a browser (a GET redirects to a short-lived download URL), for example:

```
<insta-story><img src="TEMP_BLOB_URL"></insta-story>
```

Temp-blobs are deleted automatically about a day after the upload — schedule the post within 24 hours.

**Example Input:**
```json
{
  "tempBlobUuid" : "5f0a97b2-4c31-4f5e-9b6d-2a8c0e7d1f43",
  "contentType" : "video/mp4",
  "contentLengthBytes" : 8388608
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tempBlobUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this temp-blob. Temp-blobs are immutable — when one with this UUID already exists, the result carries no `uploadUrl` since there is nothing to upload. Use `generate-uuid` to create one."
    },
    "contentType" : {
      "type" : "string",
      "description" : "The media type of the file, e.g. \"image/jpeg\" or \"video/mp4\". Only image/*, video/* and audio/* are accepted. The upload must send exactly this Content-Type header."
    },
    "contentLengthBytes" : {
      "type" : "integer",
      "minimum" : 1,
      "maximum" : 104857600,
      "description" : "The exact size of the file in bytes (max 100 MB). The upload must send exactly this Content-Length, which curl does automatically for `-T file`."
    }
  },
  "required" : [ "tempBlobUuid", "contentType", "contentLengthBytes" ]
}
```

**Example Output:**
```json
{
  "tempBlobUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "url" : "mWGpAJgu7SdK77XqbGR52q8FEX6r91",
  "uploadUrl" : "Vl7CQEx0k"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tempBlobUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this temp-blob."
    },
    "url" : {
      "type" : "string",
      "description" : "The stable URL of the temp-blob. After the upload, use it as the src inside the `html` of other procedures and in preview HTML — a GET responds with a 302 redirect to a short-lived download URL. Works until the temp-blob is deleted automatically, about a day after the upload."
    },
    "uploadUrl" : {
      "type" : "string",
      "description" : "PUT the file to this URL within 15 minutes, with the announced Content-Type header. Sandboxed environments with restricted network egress must allow the URL's storage host — when that is not possible, upload via `upload-temp-blob` instead. Absent when the temp-blob already exists — temp-blobs are immutable, so there is nothing to upload."
    }
  },
  "required" : [ "tempBlobUuid", "url" ]
}
```


### delete-draft

Deletes a draft that was created by `create-draft`. The draft disappears from the Storrito gallery.

Provide the `draftUuid` that was passed to `create-draft`.

- If the draft still exists, it is deleted and the response status is `deleted`.
- If the draft was **already deleted**, the response status is `deleted` as well (idempotent).
- If a user has already **scheduled or posted** the draft, an error is returned. Cancel the post in the Storrito app first.

When a user edited the draft in the Storrito editor, the latest saved version is deleted.

Use it to replace a draft: delete it and call `create-draft` again with a new `draftUuid` and the corrected HTML. A deleted draft still counts for the draft quota.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/delete-draft \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"draftUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "draftUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that was passed to `create-draft`. Identifies which draft to delete."
    }
  },
  "required" : [ "draftUuid" ]
}
```

**Example Output:**
```json
{
  "draftUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "deleted"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this draft."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "deleted" ],
      "description" : "Always \"deleted\": the draft was deleted or had already been deleted."
    }
  },
  "required" : [ "draftUuid", "status" ]
}
```


### generate-uuid

Generates a random UUID. Useful for creating a `storyPostUuid` before calling `schedule-instagram-story` or a `tiktokPostUuid` before calling `schedule-tiktok-post`.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/generate-uuid \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{}'
   ```

**Example Input:**
```json
{ }
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : { }
}
```

**Example Output:**
```json
{
  "uuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "uuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A randomly generated UUID."
    }
  },
  "required" : [ "uuid" ]
}
```


### list-instagram-users

Lists all Instagram users connected to your Storrito account.

   Returns an array of Instagram users with their username, numeric ID, and linked Facebook destinations. Only active (non-deleted) accounts are included. Use the `instagramUsername` value when calling `schedule-instagram-story`. Pass `shareToFacebook: true` and, when needed, one of the returned `facebookDestinationId` values to cross-post a Story to Facebook.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/list-instagram-users \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{}'
   ```

**Example Input:**
```json
{ }
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : { }
}
```

**Example Output:**
```json
{
  "instagramUsers" : [ {
    "instagramId" : "9",
    "instagramUsername" : "6xkaMjn1026d",
    "facebookDestinations" : [ {
      "facebookDestinationId" : "iyuveZri2TC152g1nVjsmJYz79PTPP42L5D3PBzcZsI60",
      "facebookDestinationName" : "F23sAiwJHpvdI86p6LJ01f0cD6Q6JzBJ8CMrJFEYN0uvDvP2teE93kkLXbxCKsAyo6t0j8PV66R5UDZbU83vbmG6Bk8Z7qrf4Z1TYl0g24y0bFdQ6mIzdEZ1awKqyk5Yd8D27f2cW43OVSIQ7Z484XLKCsnBV23HLw4aC9V1lu1WIm65Z90kQsZ26t4E3P14ZwBa",
      "facebookDestinationType" : "Ie88bay2Uj3A11CmjHO"
    }, {
      "facebookDestinationId" : "k5qu4mOv0n5OiMV883R4nwtLxM",
      "facebookDestinationName" : "623q04u8RZ6j81ciRzUSMIVWV4L7QdU8GF7864qR1EczdrOAJoy47fc1Qe115zCnn7PlSgHN4f19LWFD98ZGr8tyDown76eJI4o4lc8BAE4441E9fi1a17BY20Rxtg5g9yMck6Jp86davX2rgBcAD5o5U32UTd",
      "facebookDestinationType" : "E8grzwISIdYt5xXzA51Q9K3RxHFv6Tdxq262999glyS2ck86VAVA2B4m"
    }, {
      "facebookDestinationId" : "2mj910326ssXOuYW85tOy8hDoE5hWIMPpOAVBq1E66UezB",
      "facebookDestinationName" : "KGzeAy2GNoNQLrz2kM4796HX006c8wB7U54HGoBk10gG5Q1iCO6yQ95vHQ2jq9avQ3Nitw0KqoW349bc4809l14H3J6uM3Ih4zZRCFeBRkX1Coud1q0WLNj8V914C2woH5I70L3DU5MuzLj0AyRAbWKU5J7EzcB3roEoHe",
      "facebookDestinationType" : "4a5MiyIavT18z2"
    }, {
      "facebookDestinationId" : "u598gQ2w7Rg9I4I509oxdfsQk3P",
      "facebookDestinationName" : "BkU4HIrpw561BYemkj3C3g2831rLmHol07BUp7p6F9PdSy3lHE21X371wKiAl53200GAB47H9gwoObWCM3d0DpHMJx",
      "facebookDestinationType" : "4r292TOjHDT2HGro14Zd"
    }, {
      "facebookDestinationId" : "z2NKq23c0WHXQ90AzdzSw8q96TAlK54dQi2",
      "facebookDestinationName" : "LIK6O7t52L2u7H7Eu41ql36dRluNG7N0w4XrKiD08HGoKo1GB",
      "facebookDestinationType" : "L2jYJBQKIHPnAr5F13780"
    }, {
      "facebookDestinationId" : "0Owa9n034g85K9yX10O2BjUnKSU9TV0vD4Yy98S5",
      "facebookDestinationName" : "453Stn6p56XBTtEE360oD6v6O8e75n4UuX4YXc92q07qwFrl8nR7PTHk8EfLS4D22CLN1kE9OtJ2sAfLwnXPx830S07x4YoORbhl3zNd",
      "facebookDestinationType" : "BTK6UsIl3Ed995993p27sORVbagfHgAYFYlVjZ555FL7U69g1516tHpa3GXq"
    }, {
      "facebookDestinationId" : "9d5qL4eX84w74Nn5825pHBCIjR41WAHQ1p5kp6m07gt49Cm8E3cPi2881gB8",
      "facebookDestinationName" : "28e43s5aI20OxWgZs2149urLwa5cKm3CyZmG2WJEF9J98d55E448O76dJ3k315EbqrQS4y66ViKGtd44ohjWo9Mkp1po35x0FWUbAi2TUO44EDu52Fxba5le35SXTP6jA54dJP66uU1T3X5F4R7Pmn0f6Ps0C0xB",
      "facebookDestinationType" : "yNbBLt0HjSqDhLYA83a3KA3a92wDj4O9lk"
    }, {
      "facebookDestinationId" : "3c9",
      "facebookDestinationName" : "cENsA3qLVahtK",
      "facebookDestinationType" : "a30hMrJ84X6i04W38ec5CgtSjmyM7ug390KT5e"
    }, {
      "facebookDestinationId" : "6LC26rNOyeI6zf0wT9hTPzX16BepGM2qDuxG0K9HbopO6299",
      "facebookDestinationName" : "tCUp3uh2lE4kxqc0GAx4q0351Ai21jN43266AafY7eMZX65j3tYC20o2TFypaFe0QVtun5d87s3S6a2Fy",
      "facebookDestinationType" : "79Y7yQF7VaYptwIa49pogkhWlMHIb5"
    }, {
      "facebookDestinationId" : "D9lZGZxLK69XRt9u",
      "facebookDestinationName" : "sK3z0653Ok6ebHHAYrtr2nh1CM5KUFvAXr",
      "facebookDestinationType" : "aOS"
    }, {
      "facebookDestinationId" : "QAwMzNyp69Y7Wo9xktT5lwrQ1OZnYv718x1ztb0254lZDKu9inO9V6",
      "facebookDestinationName" : "ZYD4Hm9W3A5c",
      "facebookDestinationType" : "P6G144cXVWcq84838ZdBR2coc0"
    }, {
      "facebookDestinationId" : "4139N114ynBDJ0r3U206d23Shz3mg7PTGAlTV",
      "facebookDestinationName" : "OZFrabTrLBBzOsnqGTBJ2tUW70uc5A055XFD54np2TbhE0ZZ7JTpvxR95mqgCWl6L5MlmHsu0f18W24UFQg6OvIaZ482kbg8z9h82017uR5Yw1UIBGk0il3ch65FQ5PI3Z1vN1V95r5E1Ip2hwnDM38D2YI050eIslf5agR07dA48aSW0I",
      "facebookDestinationType" : "msDu"
    }, {
      "facebookDestinationId" : "x3wW3a4tnC2IAl8Wg1r4aMLmC9YXc1dddIKXJ9",
      "facebookDestinationName" : "xM4n2VRa",
      "facebookDestinationType" : "6492RT6gm1636w38o1kcqC8rrFK7w24M1aD3N"
    }, {
      "facebookDestinationId" : "KNcec0S564cKEjU7r20Bs2g2149H8Os9Nd0Pi3LR04xZN1L6ckpRI86DFRRqLzR2",
      "facebookDestinationName" : "442KSm8TW1btaFu8oDu61T4LEa8Mbx9X911t7K4tVXIKOevowN7hcSt1uaW9415d0jmv3s303H16hZdx1Ihye6Eyairm71SQ2z6KHZfCUr78fVEwFi32S2V596x0nwz5Zf406",
      "facebookDestinationType" : "ep"
    }, {
      "facebookDestinationId" : "gkC5U2Z8Re7Vi9KB",
      "facebookDestinationName" : "3b9JC2k7RC4MJJg1I3U9l47aT5En4QgOD526I20aV263X5JbIFS1UF31UfRBH3V9o80SgVGIg17E2K2249WkchOj7hjNmc89le1dIHjF2rf1eCuI2LrdcfQ6v3UdSTIqM2Cy63jnStDoaW7X882",
      "facebookDestinationType" : "9oX818Ru0FobK5xpn3uCnq2kUZ4n843nZFMla"
    } ]
  }, {
    "instagramId" : "4610931507493614809578541613973232763570",
    "instagramUsername" : "MYibtyzlQfiLV2nzl6"
  }, {
    "instagramId" : "663301239676142561",
    "instagramUsername" : "xFz074xuY"
  } ]
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "instagramUsers" : {
      "type" : "array",
      "items" : {
        "type" : "object",
        "properties" : {
          "instagramId" : {
            "type" : "string",
            "pattern" : "^\\d{1,50}$",
            "example" : "5940467223",
            "title" : "Instagram ID",
            "description" : "The ID used by Instagram for a entity like an user"
          },
          "instagramUsername" : {
            "type" : "string",
            "minLength" : 1,
            "maxLength" : 64,
            "example" : "maxx80000",
            "title" : "Instagram username",
            "description" : "An Instagram username"
          },
          "facebookDestinations" : {
            "type" : "array",
            "items" : {
              "type" : "object",
              "properties" : {
                "facebookDestinationId" : {
                  "type" : "string",
                  "minLength" : 1,
                  "maxLength" : 64,
                  "description" : "Facebook destination ID to pass to `schedule-instagram-story` as `facebookDestinationId`."
                },
                "facebookDestinationName" : {
                  "type" : "string",
                  "minLength" : 1,
                  "maxLength" : 200,
                  "description" : "Human-readable Facebook page or profile name."
                },
                "facebookDestinationType" : {
                  "type" : "string",
                  "minLength" : 1,
                  "maxLength" : 64,
                  "description" : "Facebook destination type passed to Instagram when cross-posting."
                }
              },
              "required" : [ "facebookDestinationId", "facebookDestinationName", "facebookDestinationType" ]
            }
          }
        },
        "required" : [ "instagramId", "instagramUsername" ]
      }
    }
  },
  "required" : [ "instagramUsers" ]
}
```


### list-tiktok-accounts

Lists all TikTok accounts connected to your Storrito account.

   Returns an array of TikTok accounts with their display name and account ID. Use the `tiktokAccountId` value when calling `schedule-tiktok-post`.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/list-tiktok-accounts \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{}'
   ```

**Example Input:**
```json
{ }
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : { }
}
```

**Example Output:**
```json
{
  "tiktokAccounts" : [ {
    "tiktokAccountId" : "9ahLj6i048eHko84tv",
    "tiktokAccountName" : "B9LYUIjhzTrI69dEjra8AlPFfn8V6vF5v0jAE7mxMrOz2516JB4O8ipTOE9uqvhh1d7058rQRSZz26s6pxNJd87t1D",
    "profilePictureUrl" : "1fW"
  } ]
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokAccounts" : {
      "type" : "array",
      "items" : {
        "type" : "object",
        "properties" : {
          "tiktokAccountId" : {
            "type" : "string",
            "minLength" : 1,
            "maxLength" : 128,
            "example" : "_000sIZJZmZpNRkCm4luQprxgrTgklr4a_ru",
            "title" : "TikTok account ID",
            "description" : "The TikTok open_id of a connected TikTok account. Use `list-tiktok-accounts` to find it."
          },
          "tiktokAccountName" : {
            "type" : "string",
            "minLength" : 1,
            "maxLength" : 100,
            "example" : "storrito03",
            "title" : "TikTok account name",
            "description" : "The display name of a connected TikTok account."
          },
          "profilePictureUrl" : {
            "type" : "string",
            "description" : "Profile picture URL of the connected TikTok account."
          }
        },
        "required" : [ "tiktokAccountId", "tiktokAccountName" ]
      }
    }
  },
  "required" : [ "tiktokAccounts" ]
}
```


### schedule-instagram-reel

Renders a page built with `<insta-story>` web components and schedules the rendered video as an Instagram reel.

   Provide either the `url` of your hosted page **or** pass the HTML directly via the `html` parameter, plus the `instagramUsername` to post to. Exactly one of `url` or `html` must be provided.

   When using `html`, the server injects the web component JavaScript and fonts automatically — just provide the raw HTML containing your `<insta-story>` markup. The HTML is loaded in a sandboxed context with no origin (about:blank), so it cannot access cookies or session data.

   Instagram reels must be videos, so the `<insta-story>` element needs a `src` attribute that points to a video. The video can be up to 3 minutes long. Any other components on the page are rendered into the video. Instagram-native sticker metadata is not available for reels.

   Optionally provide a `date` (ISO 8601 string, e.g. `"2025-06-15T14:30:00Z"`) to schedule the reel for a future time. When `date` is omitted the reel is published as soon as rendering is complete.

   Optionally provide a `caption`, set `shareToFeed` to also share the reel to the account's main feed, or set `aiGenerated` to mark the reel as AI-generated content.

   The server creates the same editor2-compatible story-media that is used by `schedule-instagram-story`, so the reel can be opened and edited in the Storrito editor. The Instagram account must be connected in your Storrito dashboard first.

   Every request requires a `reelPostUuid` that you generate on your side. This ensures idempotency — if a reel with that UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests. Pass the UUID to the `status-instagram-reel` procedure to poll for the posting status.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/schedule-instagram-reel \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "html": "<insta-story src=\"https://example.com/my-reel.mp4\"></insta-story>",
       "instagramUsername": "YOUR_INSTAGRAM_USERNAME",
       "reelPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5",
       "caption": "My first reel via the Storrito API"
     }'
   ```

**Example Input:**
```json
{
  "html" : "<insta-story src=\"https://example.com/my-reel.mp4\"></insta-story>",
  "instagramUsername" : "YOUR_INSTAGRAM_USERNAME",
  "reelPostUuid" : "3dd3f017-13cc-4dac-a2a0-876583180dd5",
  "caption" : "My first reel via the Storrito API"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "caption" : {
      "type" : "string",
      "maxLength" : 2200,
      "description" : "Optional Instagram caption for the reel. Maximum 2200 characters."
    },
    "date" : {
      "type" : "string",
      "description" : "An optional ISO 8601 date-time string (e.g. \"2025-06-15T14:30:00Z\") to schedule the reel for a future time. When omitted the reel is published as soon as rendering is complete."
    },
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this reel. Ensures idempotency — if a reel with this UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests without risk of posting the same reel twice."
    },
    "instagramUsername" : {
      "type" : "string",
      "minLength" : 1,
      "maxLength" : 64,
      "example" : "maxx80000",
      "title" : "Instagram username",
      "description" : "An Instagram username"
    },
    "shareToFeed" : {
      "type" : "boolean",
      "description" : "When true, the reel is also shared to the account's main feed. Defaults to false."
    },
    "aiGenerated" : {
      "type" : "boolean",
      "description" : "Mark the reel as AI-generated content, so Instagram shows its AI label on the reel."
    },
    "shareToFacebook" : {
      "type" : "boolean",
      "description" : "When true, the reel is recommended on Facebook. Defaults to true."
    },
    "url" : {
      "type" : "string",
      "description" : "The URL of your page built with <insta-story> web components. The server will load this URL, render the reel video, and schedule it as an Instagram reel. Provide either `url` or `html`, but not both."
    },
    "html" : {
      "type" : "string",
      "maxLength" : 10485760,
      "description" : "Inline HTML string containing your <insta-story> web components. The server injects the component JS and fonts automatically — just provide the HTML. Provide either `url` or `html`, but not both. Maximum 10 MB. Media uploaded via `create-temp-blob-upload-url` can be referenced by its temp-blob `url` in src attributes."
    }
  },
  "required" : [ "instagramUsername", "reelPostUuid" ]
}
```

**Example Output:**
```json
{
  "reelPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "scheduled"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this reel. Pass it to the `status-instagram-reel` procedure to poll for the posting status."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled" ],
      "description" : "Always \"scheduled\" for a newly created or already existing reel. Use `status-instagram-reel` to poll for the final status."
    }
  },
  "required" : [ "reelPostUuid", "status" ]
}
```


### schedule-instagram-story

Renders a page built with `<insta-story>` web components and schedules it as an Instagram story.

   Provide either the `url` of your hosted story page **or** pass the HTML directly via the `html` parameter, plus the `instagramUsername` to post to. Exactly one of `url` or `html` must be provided.

   When using `html`, the server injects the web component JavaScript and fonts automatically — just provide the raw HTML containing your `<insta-story>` markup. The HTML is loaded in a sandboxed context with no origin (about:blank), so it cannot access cookies or session data.

   Optionally provide a `date` (ISO 8601 string, e.g. `"2025-06-15T14:30:00Z"`) to schedule the story for a future time. When `date` is omitted the story is posted immediately.

   Optionally set `aiGenerated` to `true` to mark the story as AI-generated content, so Instagram shows its AI label on the story.

   Optionally set `shareToFacebook` to `true` to cross-post the Story to a Facebook destination linked to the selected Instagram account. If the account has multiple Facebook destinations, pass `facebookDestinationId`; otherwise Storrito uses the first linked destination.

   Unknown JSON keys are rejected. This prevents typos from being silently ignored.

   The server extracts sticker data from the page, builds an editor2-compatible design, and schedules the story for rendering and posting. The resulting story can be opened and edited in the Storrito editor. The Instagram account must be connected in your Storrito dashboard first.

   Every request requires a `storyPostUuid` that you generate on your side. This ensures idempotency — if a story post with that UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests. Pass the UUID to the `status-instagram-story` procedure to poll for the posting status.

   **Request duration:** Before the request returns, the server loads your page in a headless browser, extracts the sticker data and uploads the media. Depending on server load and media size (especially for video stories) this can take longer than 30 seconds, so configure a client read timeout of at least 120 seconds. If your client times out anyway, the server usually still finishes the request — retry with the same `storyPostUuid`: thanks to idempotency the retry returns the success response without creating a duplicate.

   **Extracting stickers:** Any `<insta-hashtag>`, `<insta-mention>`, `<insta-location>`, `<insta-link>`, or other sticker components on the page are automatically extracted and applied as native Instagram stickers. Unknown `<insta-*>` elements and unknown attributes on sticker elements are ignored, but reported in the `warnings` array of the response, and so are stickers that overlap each other. The story is scheduled anyway, so check `warnings`: if one names something you meant to use or a layout you did not intend, cancel the story with `cancel-instagram-story` and schedule it again with the corrected HTML.

   **Mentions:** Resolving an `<insta-mention>` username uses one of your connected Instagram accounts. If all of them are busy with another request at that moment (e.g. a story being posted), the request fails with HTTP 429. Retry it later with the same `storyPostUuid`.

   **Layout:** Put all stickers into one flex column (`display:flex;flex-direction:column;align-items:center;gap:40px` on a layer over the whole canvas, with `padding:300px 40px` for Instagram's safe zone) and let the browser space them. Sticker sizes depend on their text, so hand-picked `top` values for absolutely positioned stickers overlap easily; pill stickers alone are 177px tall.

   **Video stories:** If the `<insta-story>` element has a `src` attribute pointing to a video, a video story is created. Otherwise an image story is created.

   **Preview:** Rendering starts right after scheduling, also for stories with a future `date`. As soon as it has finished (usually within a minute), `status-instagram-story` returns a `previewUrl`: a JPEG of the rendered story with background, text and stickers (the full-size image for image stories, a 360x640 first frame for video stories). Check it while the status is still `scheduled`. If a sticker looks wrong, cancel the story with `cancel-instagram-story` and schedule it again with the corrected HTML.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/schedule-instagram-story \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "html": "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag><insta-link url=\"https://example.com\" text=\"Shop Now\"></insta-link></div></insta-story>",
       "instagramUsername": "YOUR_INSTAGRAM_USERNAME",
       "storyPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"
     }'
   ```

**Component reference.** Attribute names are exact and there is no `<insta-text>`: write text as plain HTML (`<p>`, `<h1>`, ...). Every `<insta-*>` element needs an explicit closing tag (custom elements cannot be self-closing).

- `<insta-story>`: attributes `src`. Example: `<insta-story src="https://assets.mixkit.co/videos/1164/1164-1080.mp4"></insta-story>`
- `<insta-hashtag>`: attributes `hashtag`, `design` (default/gray/rainbow). Example: `<insta-hashtag hashtag="summer" design="gray"></insta-hashtag>`
- `<insta-mention>`: attributes `username`, `design` (default/gray/rainbow). Example: `<insta-mention username="johndoe" design="gray"></insta-mention>`
- `<insta-location>`: attributes `location`, `location-id`, `design` (default/gray/black/orange/rainbow). Example: `<insta-location location="Berlin" design="gray"></insta-location>`
- `<insta-link>`: attributes `url`, `text`, `design` (default/gray/black/rainbow). Example: `<insta-link url="https://example.com" text="Shop Now" design="gray"></insta-link>`
- `<insta-poll>`: attributes `question`, `options`, `color` (black/pink/lavender/purple/orange/green/blue). Example: `<insta-poll question="Beach or Mountains?" options='["Beach", "Mountains"]' color="pink"></insta-poll>`
- `<insta-question>`: attributes `question`, `color` (white/black/pink/lavender/purple/orange/green/blue). Example: `<insta-question question="Ask me anything!" color="white"></insta-question>`
- `<insta-quiz>`: attributes `question`, `options`, `correct-answer`. Example: `<insta-quiz question="Capital of France?" options='["London", "Paris", "Berlin", "Madrid"]' correct-answer="1"></insta-quiz>`
- `<insta-countdown>`: attributes `title`, `end-time`, `color` (black/white/purple/red/orange/yellow/green/blue). Example: `<insta-countdown title="LAUNCH DAY" end-time="2026-06-15T12:00:00" color="red"></insta-countdown>`
- `<insta-emoji-slider>`: attributes `question`, `emoji`, `color`, `text-color`. Example: `<insta-emoji-slider question="How much do you like pizza?" emoji="🍕" color="#ffffff" text-color="#000000"></insta-emoji-slider>`

The full reference with key concepts (layout, safe zone, sticker sizes, fonts) is at https://storrito.com/documentation/api/v1/story-components.md (MCP clients: call the `get-story-components-reference` tool).

**Example Input:**
```json
{
  "html" : "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag><insta-link url=\"https://example.com\" text=\"Shop Now\"></insta-link></div></insta-story>",
  "instagramUsername" : "YOUR_INSTAGRAM_USERNAME",
  "storyPostUuid" : "3dd3f017-13cc-4dac-a2a0-876583180dd5"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "url" : {
      "type" : "string",
      "description" : "The URL of your page built with <insta-story> web components. The server will load this URL, render the story, and extract any sticker metadata. Provide either `url` or `html`, but not both."
    },
    "html" : {
      "type" : "string",
      "maxLength" : 10485760,
      "description" : "Inline HTML string containing your <insta-story> web components. The server injects the component JS and fonts automatically — just provide the HTML. Provide either `url` or `html`, but not both. Maximum 10 MB. Media uploaded via `create-temp-blob-upload-url` can be referenced by its temp-blob `url` in src attributes."
    },
    "instagramUsername" : {
      "type" : "string",
      "minLength" : 1,
      "maxLength" : 64,
      "example" : "maxx80000",
      "title" : "Instagram username",
      "description" : "An Instagram username"
    },
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this story post. Ensures idempotency — if a story post with this UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests without risk of posting the same story twice."
    },
    "date" : {
      "type" : "string",
      "description" : "An optional ISO 8601 date-time string (e.g. \"2025-06-15T14:30:00Z\") to schedule the story for a future time. When omitted the story is posted immediately."
    },
    "aiGenerated" : {
      "type" : "boolean",
      "description" : "Mark the story as AI-generated content, so Instagram shows its AI label on the story."
    },
    "shareToFacebook" : {
      "type" : "boolean",
      "description" : "When true, cross-posts the Story to a Facebook destination linked to the Instagram account. If `facebookDestinationId` is omitted, Storrito uses the first linked Facebook destination for that Instagram account."
    },
    "facebookDestinationId" : {
      "anyOf" : [ {
        "type" : "string"
      }, {
        "type" : "integer"
      } ],
      "description" : "Optional Facebook destination ID to use when `shareToFacebook` is true. The destination must be linked to the selected Instagram account."
    }
  },
  "required" : [ "instagramUsername", "storyPostUuid" ]
}
```

**Example Output:**
```json
{
  "storyPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "scheduled",
  "warnings" : [ "9u21pO9PGXutn1uyr5i", "7ZgTGOQ4trbm0v0jmZ0TaO9fC1a", "S", "qVh4clHvC7DeFdw1", "lLNL4NN97o5J9O9H5NVD", "z73q75tZeZ923", "4A4PK5Yqq6u2c400BGv72inv1b9ut1", "xIktpNM", "wkoiYQbByQI7k7HPnpkW7v9w5dd" ]
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this story post. Pass it to the `status-instagram-story` procedure to poll for the posting status."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled" ],
      "description" : "Always \"scheduled\" for a newly created or scheduled story. Use `status-instagram-story` to poll for the final status."
    },
    "warnings" : {
      "type" : "array",
      "items" : {
        "type" : "string"
      },
      "description" : "Only present when the HTML contained unknown `<insta-*>` elements or unknown attributes on sticker elements (they were ignored), or when two stickers overlap on the canvas. The story was scheduled anyway. Check every warning: if it names something you meant to use or a layout you did not intend, cancel the story with `cancel-instagram-story` and schedule it again with the corrected HTML."
    }
  },
  "required" : [ "storyPostUuid", "status" ]
}
```


### schedule-tiktok-post

Renders a page built with `<insta-story>` web components and schedules the rendered media as a TikTok post.

   Provide either the `url` of your hosted story page **or** pass the HTML directly via the `html` parameter, plus the `tiktokAccountId` to post to. Exactly one of `url` or `html` must be provided.

   When using `html`, the server injects the web component JavaScript and fonts automatically — just provide the raw HTML containing your `<insta-story>` markup. The HTML is loaded in a sandboxed context with no origin (about:blank), so it cannot access cookies or session data.

   Optionally provide a `date` (ISO 8601 string, e.g. `"2025-06-15T14:30:00Z"`) to schedule the TikTok post for a future time. When `date` is omitted the post is published as soon as rendering is complete.

   For rendered image/photo posts, TikTok allows `title` to be at most 90 characters. Put longer text into `description`.

   The server creates the same editor2-compatible story-media that is used by `schedule-instagram-story`. TikTok receives the rendered image or video; Instagram-native sticker metadata is not sent to TikTok.

   Every request requires a `tiktokPostUuid` that you generate on your side. This ensures idempotency — if a TikTok post with that UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests. Pass the UUID to the `status-tiktok-post` procedure to poll for the posting status.

   Use `list-tiktok-accounts` to find the `tiktokAccountId` of a connected account.

   ```
   curl -X POST https://ORG_UUID.storrito.com/api/v1/schedule-tiktok-post \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "html": "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag><insta-mention username=\"storrito\"></insta-mention></div></insta-story>",
       "tiktokAccountId": "YOUR_TIKTOK_ACCOUNT_ID",
       "tiktokPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"
     }'
   ```

**Example Input:**
```json
{
  "html" : "<insta-story><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e\"></div><div style=\"position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:300px 40px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:40px\"><insta-hashtag hashtag=\"travel\"></insta-hashtag><insta-mention username=\"storrito\"></insta-mention></div></insta-story>",
  "tiktokAccountId" : "YOUR_TIKTOK_ACCOUNT_ID",
  "tiktokPostUuid" : "3dd3f017-13cc-4dac-a2a0-876583180dd5"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "description" : {
      "type" : "string",
      "maxLength" : 2200,
      "description" : "Optional TikTok description."
    },
    "date" : {
      "type" : "string",
      "description" : "An optional ISO 8601 date-time string (e.g. \"2025-06-15T14:30:00Z\") to schedule the TikTok post for a future time. When omitted the post is published as soon as rendering is complete."
    },
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this TikTok post. Ensures idempotency — if a TikTok post with this UUID already exists, the same success response is returned without creating a duplicate. This makes it safe to retry requests without risk of posting the same TikTok twice."
    },
    "videoCoverTimestampMs" : {
      "type" : "integer",
      "minimum" : 0,
      "description" : "Video frame timestamp in milliseconds used as the TikTok cover. TikTok uses the first frame when omitted or invalid."
    },
    "disableDuet" : {
      "type" : "boolean",
      "description" : "Disable duets for the TikTok post."
    },
    "brandOrganic" : {
      "type" : "boolean",
      "description" : "Enable TikTok's brand organic disclosure toggle."
    },
    "disableComment" : {
      "type" : "boolean",
      "description" : "Disable comments for the TikTok post."
    },
    "brandedContent" : {
      "type" : "boolean",
      "description" : "Enable TikTok's branded content disclosure toggle."
    },
    "privacyLevel" : {
      "type" : "string",
      "enum" : [ "PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "SELF_ONLY" ],
      "description" : "TikTok privacy level. Defaults to SELF_ONLY."
    },
    "title" : {
      "type" : "string",
      "maxLength" : 2200,
      "description" : "Optional TikTok title/caption. TikTok photo posts allow at most 90 characters; video captions can be longer."
    },
    "aiGenerated" : {
      "type" : "boolean",
      "description" : "Mark the post as AI-generated content."
    },
    "autoAudio" : {
      "type" : "boolean",
      "description" : "Let TikTok automatically add audio to photo posts."
    },
    "tiktokAccountId" : {
      "type" : "string",
      "minLength" : 1,
      "maxLength" : 128,
      "example" : "_000sIZJZmZpNRkCm4luQprxgrTgklr4a_ru",
      "title" : "TikTok account ID",
      "description" : "The TikTok open_id of a connected TikTok account. Use `list-tiktok-accounts` to find it."
    },
    "url" : {
      "type" : "string",
      "description" : "The URL of your page built with <insta-story> web components. The server will load this URL, render the story, and schedule the rendered media as a TikTok post. Provide either `url` or `html`, but not both."
    },
    "disableStitch" : {
      "type" : "boolean",
      "description" : "Disable stitches for the TikTok post."
    },
    "html" : {
      "type" : "string",
      "maxLength" : 10485760,
      "description" : "Inline HTML string containing your <insta-story> web components. The server injects the component JS and fonts automatically — just provide the HTML. Provide either `url` or `html`, but not both. Maximum 10 MB. Media uploaded via `create-temp-blob-upload-url` can be referenced by its temp-blob `url` in src attributes."
    }
  },
  "required" : [ "tiktokAccountId", "tiktokPostUuid" ]
}
```

**Example Output:**
```json
{
  "tiktokPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "scheduled"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this TikTok post. Pass it to the `status-tiktok-post` procedure to poll for the posting status."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled" ],
      "description" : "Always \"scheduled\" for a newly created or already existing TikTok post. Use `status-tiktok-post` to poll for the final status."
    }
  },
  "required" : [ "tiktokPostUuid", "status" ]
}
```


### status-draft

Returns the current status of a draft that was created by `create-draft`.

Provide the `draftUuid` that was passed to `create-draft`. The response includes a `status` field:

- `rendering` — the draft is in the gallery, its preview is not rendered yet.
- `ready` — the draft is rendered. The `previewUrl` field contains a JPEG of the rendered design.
- `failed` — the rendering failed for good. Delete the draft with `delete-draft` and create it again.
- `deleted` — the draft was deleted, in the Storrito app or via `delete-draft`.

Rendering starts right after `create-draft` and usually finishes within a minute. Poll until the status is `ready` and check the `previewUrl`. If something looks wrong, delete the draft with `delete-draft` and create it again with corrected HTML.

When a user edits the draft in the Storrito editor, this procedure follows the edit: status, `previewUrl` and `editorUrl` describe the latest saved version.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/status-draft \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"draftUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "draftUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that was passed to `create-draft`. Identifies which draft to check."
    }
  },
  "required" : [ "draftUuid" ]
}
```

**Example Output:**
```json
{
  "draftUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "ready",
  "editorUrl" : "qP"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "draftUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this draft."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "rendering", "ready", "failed", "deleted" ],
      "description" : "The current status: \"rendering\" (the preview is not ready yet), \"ready\" (rendered, see previewUrl), \"failed\" (the rendering failed for good) or \"deleted\" (deleted in the Storrito app or via `delete-draft`)."
    },
    "previewUrl" : {
      "type" : "string",
      "description" : "URL of a JPEG of the rendered draft (background, text and stickers): the full-size image for image designs, a 360x640 first frame for video designs. Present as soon as the rendering has finished, usually within a minute after `create-draft`."
    },
    "editorUrl" : {
      "type" : "string",
      "description" : "URL that opens the draft in the Storrito editor. It requires a login to the Storrito workspace. Not present when the draft is deleted."
    }
  },
  "required" : [ "draftUuid", "status" ]
}
```


### status-instagram-reel

Returns the current posting status of an Instagram reel.

Provide the `reelPostUuid` that was returned by the `schedule-instagram-reel` procedure. The response includes a `status` field:

- `scheduled` — the reel is queued or its video is still rendering.
- `in-progress` — the reel is due and being processed.
- `executed` — the reel has been posted to Instagram.
- `failed` — posting failed. The `errorMessage` field contains details.
- `canceled` — the reel was canceled before it was posted.

Call this procedure repeatedly to poll until the status is `executed` or `failed`.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/status-instagram-reel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reelPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "reelPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID that identifies a scheduled Instagram reel."
    }
  },
  "required" : [ "reelPostUuid" ]
}
```

**Example Output:**
```json
{
  "reelPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "in-progress",
  "errorMessage" : "Vl7CQEx0k"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "reelPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this reel."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled", "in-progress", "executed", "failed", "canceled" ],
      "description" : "The current posting status: \"scheduled\" (queued or rendering), \"in-progress\" (due and being processed), \"executed\" (posted to Instagram), \"failed\" (see errorMessage), or \"canceled\"."
    },
    "errorMessage" : {
      "type" : "string",
      "description" : "A human-readable error description. Only present when status is \"failed\"."
    }
  },
  "required" : [ "reelPostUuid", "status" ]
}
```


### status-instagram-story

Returns the current posting status of an Instagram story.

Provide the `storyPostUuid` that was returned by the `schedule-instagram-story` procedure. The response includes a `status` field:

- `scheduled` — the story is queued and will be posted shortly.
- `executed` — the story has been posted to Instagram.
- `failed` — posting failed. The `errorMessage` field contains details.
- `canceled` — the story post was canceled before it was posted.

Call this procedure repeatedly to poll until the status is `executed` or `failed`.

**Preview:** Rendering starts right after scheduling. As soon as it has finished (usually within a minute), the response contains a `previewUrl`: a JPEG of the rendered story with background, text and stickers (the full-size image for image stories, a 360x640 first frame for video stories). Poll until it is present and check the design while the status is still `scheduled`. If a sticker looks wrong, cancel the story with `cancel-instagram-story` and schedule it again with the corrected HTML.

**How long a story stays `scheduled`:** After `schedule-instagram-story` returns, the story is rendered and then posted to Instagram, starting at the scheduled `date` (or immediately when no `date` was given). The time from `scheduled` to `executed` therefore varies with rendering time and Instagram's availability — typically it is under a couple of minutes, but there is no fixed duration, so keep polling instead of assuming a failure after a short delay. The hard upper bound is 60 minutes: a story that could not be posted within 60 minutes after its scheduled time is never posted late, it is marked as `failed` instead.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/status-instagram-story \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storyPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "storyPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID returned by `schedule-instagram-story`. Identifies which story post to check."
    }
  },
  "required" : [ "storyPostUuid" ]
}
```

**Example Output:**
```json
{
  "storyPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "executed",
  "previewUrl" : "qP"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "storyPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this story post."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled", "executed", "failed", "canceled" ],
      "description" : "The current posting status: \"scheduled\" (queued, will be posted shortly), \"executed\" (posted to Instagram), \"failed\" (see errorMessage), or \"canceled\"."
    },
    "errorMessage" : {
      "type" : "string",
      "description" : "A human-readable error description. Only present when status is \"failed\"."
    },
    "previewUrl" : {
      "type" : "string",
      "description" : "URL of a JPEG of the rendered story (background, text and stickers): the full-size image for image stories, a 360x640 first frame for video stories. Present as soon as the rendering has finished, usually within a minute after scheduling. Open it to verify the design while the status is still \"scheduled\"; if something is off, cancel the story with `cancel-instagram-story` and schedule it again with corrected HTML."
    }
  },
  "required" : [ "storyPostUuid", "status" ]
}
```


### status-tiktok-post

Returns the current posting status of a TikTok post.

Provide the `tiktokPostUuid` that was returned by the `schedule-tiktok-post` procedure. The response includes a `status` field:

- `scheduled` — the post is queued or its media is still rendering.
- `in-progress` — the post is due and being processed.
- `executed` — TikTok confirmed that publishing completed.
- `failed` — posting failed. The `errorMessage` field contains details.
- `canceled` — the TikTok post was canceled before it was posted.

Call this procedure repeatedly to poll until the status is `executed` or `failed`.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/status-tiktok-post \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tiktokPostUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5"}'
```

**Example Input:**
```json
{
  "tiktokPostUuid" : "4b52922a-3341-4580-adc2-edb475d712c2"
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID that identifies a TikTok post."
    }
  },
  "required" : [ "tiktokPostUuid" ]
}
```

**Example Output:**
```json
{
  "tiktokPostUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "status" : "in-progress",
  "tiktokStatus" : "qP"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tiktokPostUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this TikTok post."
    },
    "status" : {
      "type" : "string",
      "enum" : [ "scheduled", "in-progress", "executed", "failed", "canceled" ],
      "description" : "The current posting status: \"scheduled\" (queued or rendering), \"in-progress\" (due and being processed), \"executed\" (accepted by TikTok), \"failed\" (see errorMessage), or \"canceled\"."
    },
    "errorMessage" : {
      "type" : "string",
      "description" : "A human-readable error description. Only present when status is \"failed\"."
    },
    "tiktokStatus" : {
      "type" : "string",
      "description" : "The last raw publish status returned by TikTok, if available."
    }
  },
  "required" : [ "tiktokPostUuid", "status" ]
}
```


### upload-temp-blob

Uploads a temporary media blob inline as base64 (image, video or audio, max 30 MB decoded).

Use this only when your environment cannot PUT to the presigned URL of `create-temp-blob-upload-url` — for example a sandboxed AI agent whose network egress does not allow the storage host. The media passes through the API request itself, so prefer the presigned upload whenever possible, especially for videos.

```
curl -X POST https://ORG_UUID.storrito.com/api/v1/upload-temp-blob \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tempBlobUuid": "3dd3f017-13cc-4dac-a2a0-876583180dd5", "contentType": "image/jpeg", "contentBase64": "'$(base64 -w0 photo.jpg)'"}'
```

Afterwards use the stable `url` from the result as the src inside the `html` of `schedule-instagram-story`, `schedule-instagram-reel` or `schedule-tiktok-post`, and to preview the media in a browser.

**Example Input:**
```json
{
  "tempBlobUuid" : "5f0a97b2-4c31-4f5e-9b6d-2a8c0e7d1f43",
  "contentType" : "image/jpeg",
  "contentBase64" : "/9j/4AAQSkZJRgABAQ..."
}
```

**Input Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tempBlobUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "A UUID you provide to identify this temp-blob. Temp-blobs are immutable — when one with this UUID already exists, the upload is ignored and the same success response is returned. Use `generate-uuid` to create one."
    },
    "contentBase64" : {
      "type" : "string",
      "maxLength" : 41943040,
      "description" : "The base64-encoded bytes of the image, video or audio file (max 30 MB decoded)."
    },
    "contentType" : {
      "type" : "string",
      "description" : "The media type of the blob, e.g. \"image/jpeg\" or \"video/mp4\". Only image/*, video/* and audio/* are accepted."
    }
  },
  "required" : [ "tempBlobUuid", "contentBase64", "contentType" ]
}
```

**Example Output:**
```json
{
  "tempBlobUuid" : "5e0b18ce-66b5-4d62-a452-50b6004b6d01",
  "url" : "mWGpAJgu7SdK77XqbGR52q8FEX6r91"
}
```

**Output Schema:**
```json
{
  "type" : "object",
  "properties" : {
    "tempBlobUuid" : {
      "type" : "string",
      "format" : "uuid",
      "description" : "The UUID that identifies this temp-blob."
    },
    "url" : {
      "type" : "string",
      "description" : "The stable URL of the temp-blob. Use it as the src inside the `html` of other procedures and in preview HTML — a GET responds with a 302 redirect to a short-lived download URL. Works until the temp-blob is deleted automatically, about a day after the upload."
    }
  },
  "required" : [ "tempBlobUuid", "url" ]
}
```

