# Instagram carousel from a brief

One call turns a short brief into a finished Instagram carousel: 5–10 slides, **1080×1080 (1:1) JPEG — the feed's
square, the default**, designed Hebrew or English text over a photo, a generated picture or a brand-colour card — with
the caption, at most 5 hashtags, alt text per slide and the source of every picture. `aspect: "4:5"` makes the taller
1080×1350 slide instead: it takes more of the feed, and holds a little more text. The text is set by our render engine, never drawn by an image model, so
Hebrew, numbers, prices and @handles come out right.

Base URL: https://apihub.co.il · every call carries `Authorization: Bearer <API key>`.

## The six kinds of carousel — each a skill of its own

Each has its own recipe with the request preset for that kind, a real example and its own price:

| skill | kind | when to use it | the example | base credits |
|---|---|---|---|---|
| [`carousel/tips`](https://apihub.co.il/skills/carousel-tips.md) | Tips (טיפים) | A short list of tips, one per slide. The post people save. | `stock` + `photo`, 7 stock photos | 36 |
| [`carousel/myths`](https://apihub.co.il/skills/carousel-myths.md) | Myths vs. facts (מיתוסים מול עובדות) | What everyone believes and what is true: a myth, then its truth. | `ai` + `card`, one generated picture (the cover) | 58 |
| [`carousel/steps`](https://apihub.co.il/skills/carousel-steps.md) | Step-by-step guide (מדריך צעד אחר צעד) | A process in order, one step per slide. For how-tos and questions that keep coming. | `ai` + `photo`, 7 generated pictures | 229 |
| [`carousel/before-after`](https://apihub.co.il/skills/carousel-before-after.md) | Before and after (לפני ואחרי) | The result of a job: before, what you did, after. Best with your own pictures. | `ai` + `split`, 7 generated pictures | 223 |
| [`carousel/story`](https://apihub.co.il/skills/carousel-story.md) | Story (סיפור) | Your story or a customer's: how it began, what changed, what you learned. | `stock` + `card`, one stock photo (the cover) | 32 |
| [`carousel/product`](https://apihub.co.il/skills/carousel-product.md) | Products (מוצרים) | A product or service on every slide, and how to order at the end. | `stock` + `split`, 7 stock photos | 35 |

This file is the general recipe: every field, every look and picture level. When the user picked a kind, follow its own file.

## What to ask the user

Only the **brief**: the topic (or their own text to turn into slides), the business's name, and what the last slide
should ask for (save, follow, send a message). Facts, prices, dates and phone numbers are taken only from the brief —
nothing is invented — so anything that must appear on a slide has to be in it.

Optional, only if they have them: brand colours (`primary`, `accent` as #RRGGBB), a logo (`logo_url`: PNG / JPEG /
WebP), the Instagram `handle`. Without colours the planner picks a palette that fits the topic.

Do not ask about slide counts, looks or picture levels — use the defaults below unless the user asked for something else.

## 1. The request

```jsonc
POST /v1/carousel
{
  "brief": "5 טעויות בצחצוח שיניים שכמעט כולם עושים, ואיך לתקן אותן. מרפאת שיניים ד״ר כהן ברמת גן, לילדים ולמבוגרים. קביעת תור בוואטסאפ.",
  "language": "he",                  // he (default) | en
  "slides": 7,                       // 5–10, default 7
  "aspect": "1:1",                   // 1:1 (default, 1080×1080) | 4:5 (1080×1350: taller, takes more of the feed)
  "images": "stock",                 // stock (default) | ai | ai-pro | none
  "look": "photo",                   // auto (default) | photo | card | split
  "brand": { "handle": "dr.cohen.dental" }   // + primary, accent, logo_url, font — all optional
}
```

- `aspect`: **1:1** = the square, 1080×1080 — the default; leave the field out. **4:5** = 1080×1350, only when the
  user asks for the taller post. The square holds less text (a headline up to 6 words, a body up to 18; 4:5: 8 and 25)
  — the planner writes to the shape, there is nothing to shorten yourself. The price is the same in both shapes.
- `images`: **stock** = free photos, picked and checked for each slide (no faces, no brands); a slide with no good
  photo becomes a brand-colour card — never a paid generation. **ai** = generated pictures, the cover first and the
  rest in its style. **ai-pro** = the stronger image model. **none** = brand-colour cards only.
- `look`: **photo** = a full-bleed picture on every slide. **card** = brand colour and big numbers, a picture on the
  cover only (the cheapest with `ai`: one generated picture). **split** = picture on top in a rounded frame, text
  below. `auto` = photo with stock, card with generated pictures or none.
- `own_images`: up to 10 picture URLs of the user's own, used first, in order (slide 1 gets the first). **For a
  product carousel or a before-and-after use them**: a stock photo only resembles the product, it is not the product.
- `archetype`: auto | tips | myths | steps | before-after | story | product (the six kinds above; `auto` lets the
  planner choose from the brief). `audience`, `tone`, `cta`: optional short strings.
- The price before running: `POST /v1/carousel/estimate` with the same body → `estimated_credits` (the worst case,
  what is reserved; the charge is what really ran).

The call answers **202** with the execution: `{ "id": "<execution id>", "status": "processing", "output": { "status": "processing", "stage": "planning" } }`.

## 2. Poll

```jsonc
GET /v1/executions/<execution id>      // every 3 seconds; 202 while it runs, 200 when done
```

`output.stage` goes planning → images → rendering. Measured on the real examples: 22–39 s with stock photos,
34 s with one generated picture, 53–54 s with seven. Allow three minutes before giving up. `status: "failed"` carries a
plain reason in `error` (a plan that could not keep the limits, a picture link that cannot be fetched) — nothing or
only what was really spent is charged.

## 3. The result

```jsonc
{
  "status": "completed",
  "output": {
    "slides": [ { "n": 1, "url": "https://…/slide-01.jpg", "alt": "…", "role": "hook", "headline": "…", "body": "…",
                  "image": { "source": "stock", "provider": "pexels", "licence": "Pexels License", "licence_url": "…", "author": "…", "page_url": "…" } } ],
    "caption": "…", "hashtags": ["#…"],          // at most 5
    "plan": { … },                                 // keep it: it is how one slide is fixed
    "look": "photo", "archetype": "tips", "aspect": "1:1", "size": { "width": 1080, "height": 1080 },
    "images": { "mode": "stock", "stock": 7, "ai": 0, "own": 0, "cards": 0 },
    "expires_at": "…",                             // 24 hours
    "warnings": []
  },
  "usage": { "credits_charged": 36 }       // the example's base API price (the price section below)
}
```

Show the user every slide, in order, with the caption. Read `warnings`: it says which slide got a brand-colour card
instead of a photo, or that a logo could not be read. **Download the slides within 24 hours** — the files expire.

## 4. Fix one slide (no pictures are paid for again)

Change the words (or one picture) in `output.plan` and render again — no model call, no picture search:

```jsonc
POST /v1/carousel/render
{ "plan": <output.plan with your edit>, "only": [3] }      // only: the slides to render (1-based); leave it out for all
```

- a word, a headline, a body: edit `plan.slides[2].headline` / `.body` — within the limits of the carousel's shape
  (square: headline ≤ 6 words and 44 characters, body ≤ 18 words and 120 characters; 4:5: headline ≤ 8 words, body ≤ 25);
- another picture: set `plan.slides[2].image_url` to the user's own picture URL; remove the field for a brand-colour card;
- a picture whose subject is cut by the crop: set `plan.slides[2].focus_y` (0 = keep the picture's top, 1 = its bottom)
  or `focus_x` (0 = its left, 1 = its right);
- colours or font for the whole carousel: edit `plan.brand` and render without `only`;
- the other shape: send `"aspect": "4:5"` (or `"1:1"`) next to the plan, without `only` — the whole carousel is rendered
  again in that shape from the same pictures (a carousel has one shape; the plan keeps its `aspect`, so a plain
  re-render never changes it).

Poll the same way. A re-rendered slide keeps its place: its marker still says 3/7 and its layout matches the others.
The charge is the render only (base price: 7 slides 21 credits, one slide 3).

## 5. Publish (optional)

Publishing needs the account's social plan (connected networks; 1,500 credits per 30 days). Check it first, it is free:

```jsonc
GET /v1/social/plan
// → { "active": true|false, "active_until": "…", "monthly_credits": 1500, "period_days": 30, "how_to_activate": "…" }
```

If `active` is false, tell the user the price and ask before turning it on: `POST /v1/social/plan { "confirm": true }`
charges the 1,500 credits once (`charged_credits` is 0 when the plan is already on). Without the plan the publish call
answers `402 social_plan_inactive` with the price and charges nothing. A user who only wants the files needs no plan:
give them the slide links and the caption.

```jsonc
POST /v1/social/publish
{ "text": "<caption>\n\n<hashtags joined by spaces>", "media": [ "<slide 1 url>", "<slide 2 url>", … ], "profiles": ["instagram"] }
```

The slide URLs go in `media` in order (Instagram takes 2–10 pictures in a carousel; ours are JPEG, square 1080×1080 —
or 1080×1350 when made with `aspect: "4:5"` — under 8 MB, all one size). **Publishing a carousel through this endpoint has not been verified live by us yet** — if the post
comes back as something other than a carousel, give the user the files and the caption to post by hand, and say so.

## Prices (measured on real runs, 7 slides unless noted)

| What | Base credits | Time |
|---|---|---|
| `stock` + `card` (one stock photo (the cover)) | 32 | 29 s |
| `stock` + `split` (7 stock photos) | 35 | 39 s |
| `stock` + `photo` (7 stock photos) | 36 | 38 s |
| `ai` + `card` (one generated picture (the cover)) | 58 | 34 s |
| `ai` + `split` (7 generated pictures) | 223 | 54 s |
| `ai` + `photo` (7 generated pictures) | 229 | 53 s |
| `stock` + `card`, 6 slides (one stock photo (the cover)) | 26 | 22 s |
| re-render, all 7 slides (also: the same carousel in the other shape) | 21 | 2–7 s |
| re-render, one slide | 3 | 4–12 s |

Every number here is the **base API price**: the real cost of what ran — the planner, the photo check or each generated
picture, the render — × 3, in credits, rounded up to a whole credit. An account that bought its credits
at a package discount pays the same money and therefore more credits per call. **`POST /v1/carousel/estimate` with the
same body answers with the price for the account that calls it**: `estimated_credits` is the ceiling (what is reserved);
the charge is what really ran — on these runs about half of it.

`ai-pro` pictures cost more than `ai` (ask `/estimate`). Generated pictures are sold per picture, so the cheap way to
a generated look is `card` (one picture), not `photo` or `split` (one per slide).

## Output rules

- Post the slides in order, all of them — slide 1 is the hook, slide 2 stands on its own, the last one is the ask.
- Never put text into the pictures yourself and never ask an image model to: every word on a slide comes from the plan.
- At most 5 hashtags (Instagram's limit); the caption stays under 2,200 characters. Use `slides[].alt` as the alt text.
- Stock pictures need no credit on the slide; `slides[].image` holds each one's source, author and licence — keep it.
- A carousel is 2–10 pictures of one size. Do not mix these slides with pictures of another shape, and never mix
  square and 4:5 slides in one post: Instagram crops every picture of a carousel to the shape of the first.

## Examples (real runs)

The slides are square, 1080×1080 — what the call makes by default. Each example is one real run; its slides are shown
as the run's own plan and pictures rendered in the square (`POST /v1/carousel/render` with `"aspect": "1:1"`), and its
price and time are the full run's (planner, pictures, render) — the same in both shapes.

- **5 common tooth-brushing mistakes** (tips) — `stock` + `photo`, 7 slides 1080×1080 (7 stock photos), 36 credits, 38 s: https://media.gpt.org.il/lookbook/examples/carousel/dentist-stock-photo/square/slide-01.jpg … slide-07.jpg
- **Our five best-loved bakes** (product) — `stock` + `split`, 7 slides 1080×1080 (7 stock photos), 35 credits, 39 s: https://media.gpt.org.il/lookbook/examples/carousel/bakery-stock-split/square/slide-01.jpg … slide-07.jpg
- **3 myths about strength training for women** (myths) — `ai` + `card`, 7 slides 1080×1080 (one generated picture (the cover)), 58 credits, 34 s: https://media.gpt.org.il/lookbook/examples/carousel/fitness-ai-card/square/slide-01.jpg … slide-07.jpg
- **The way to your first apartment** (steps) — `ai` + `photo`, 7 slides 1080×1080 (7 generated pictures), 229 credits, 53 s: https://media.gpt.org.il/lookbook/examples/carousel/realestate-ai-photo/square/slide-01.jpg … slide-07.jpg
- **Ready in five minutes every morning** (before-after) — `ai` + `split`, 7 slides 1080×1080 (7 generated pictures), 223 credits, 54 s: https://media.gpt.org.il/lookbook/examples/carousel/organizer-ai-split/square/slide-01.jpg … slide-07.jpg
- **How I left tech for ceramics** (story) — `stock` + `card`, 7 slides 1080×1080 (one stock photo (the cover)), 32 credits, 29 s: https://media.gpt.org.il/lookbook/examples/carousel/ceramics-stock-card/square/slide-01.jpg … slide-07.jpg
- **Four steps to better coffee** (steps) — `stock` + `card`, 6 slides 1080×1080 (one stock photo (the cover)), 26 credits, 22 s: https://media.gpt.org.il/lookbook/examples/carousel/coffee-en-stock-card/square/slide-01.jpg … slide-06.jpg
