domi reels

The Domi Reels API

Your restaurants already have their menus and food photos on your platform. Connect them to Domi Reels and Meta's Muse directs a Reel for each one, every week, that keeps their real food. Doocado is the first platform connected.

Overview

There are two halves, and you need both:

  • You call us (API v1, below): tell Domi Reels which restaurants to make Reels for and when, ask for a Reel now, and read the Reels back.
  • We call you (the contract): before a Reel, Domi Reels asks your kit url for the restaurant's menu and photos. When the Reel is ready, it hands it to your drop url, and you send it to the owner on your own channel.
ordering platform                              Domi Reels
-----------------                              ----------
PUT  /api/v1/restaurants/8      ───────────▶   restaurant 8: Sundays at 8 PM, 1 Reel a week
                                               (or POST /api/v1/restaurants/8/reels for one now)
kit  ◀───────────────────────────────────────  "what's on the menu at 8?"
     ───────────────────────────────────────▶  menu + owner notes + the restaurant's photos
                                               Muse looks at every photo, pitches, plans,
                                               films takes, keeps the good seconds, cuts,
                                               scores, writes the text, gives it a last look
drop ◀───────────────────────────────────────  the Reel, its cover, the owner's message
GET  /api/v1/reels?restaurant=8 ───────────▶   the same Reels, any time, with download urls

Getting connected

Domi Reels is by invitation only, for platforms too. When we connect you:

  • We give you an API key once. We keep only its SHA-256 hash, so store it somewhere safe; a lost key is replaced, not recovered.
  • You give us your kit url, your drop url and a token. We send that token on every call to you as x-reels-token, so you can keep only its hash too.

Every request and response is JSON. The base url is https://domi-reels.vercel.app.

Authentication

Send your key on every call:

curl https://domi-reels.vercel.app/api/v1/restaurants \
  -H "Authorization: Bearer $DOMI_REELS_KEY"

A missing or wrong key answers 401. A key only ever sees its own platform's restaurants, jobs and Reels.

Restaurants

A restaurant is known by your id for it, external_id: whatever your platform uses (letters, digits, _ - . :, up to 64). That same id is what we send back to your kit and drop.

PUT/api/v1/restaurants/{external_id}

Creates the restaurant, or updates the fields you send. 201 when it is new, 200 when it already existed. Send only what changed; a new restaurant needs at least a name.

FieldTypeDefaultMeaning
namestringThe restaurant's name, as customers know it.
cuisinestringnull"Empanadas, sandwiches". Muse reads it.
citystringnullWhere it is.
languageen | esenThe language of the Reel's words and the owner's message.
timezoneIANA nameAmerica/New_YorkDays and hours are counted here.
delivery_day0 to 60The day the week's Reels go out. 0 is Sunday.
delivery_hour0 to 2320The hour they go out, in the restaurant's timezone.
reels_per_week0 to 31How many Reels a week. 0 pauses the weekly Reels; a Reel on demand still works.
curl -X PUT https://domi-reels.vercel.app/api/v1/restaurants/8 \
  -H "Authorization: Bearer $DOMI_REELS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Pikalo", "cuisine": "Empanadas, Sandwiches", "city": "Jamaica Plain",
        "language": "en", "timezone": "America/New_York",
        "delivery_day": 0, "delivery_hour": 20, "reels_per_week": 1 }'
{
  "restaurant": {
    "external_id": "8", "name": "Pikalo", "cuisine": "Empanadas, Sandwiches", "city": "Jamaica Plain",
    "language": "en", "timezone": "America/New_York", "delivery_day": 0, "delivery_hour": 20,
    "reels_per_week": 1, "active": true, "created_at": "2026-11-04T06:00:00.000Z"
  },
  "created": true
}
GET/api/v1/restaurants

All your restaurants: { "restaurants": [ ... ] }, each in the shape above.

GET/api/v1/restaurants/{external_id}

One restaurant: { "restaurant": { ... } }, or 404.

Reels

POST/api/v1/restaurants/{external_id}/reels

Asks Muse for a Reel now, outside the weekly schedule. No body needed. Answers 202 with the job to follow; the Reel is dropped to your drop url as soon as Muse approves it. One Reel in the making per restaurant (a second ask answers 409 with the job already running), at most 3 per restaurant and 20 per account in 24 hours (429).

curl -X POST https://domi-reels.vercel.app/api/v1/restaurants/8/reels -H "Authorization: Bearer $DOMI_REELS_KEY"
{
  "job": {
    "id": "6f1c2a0e-8d1b-4c55-9a7e-3b9f0d2e4a11", "restaurant": "8", "kind": "reel", "status": "queued",
    "week": "2026-11-04", "questions": [], "error": null, "reel_id": null,
    "created_at": "2026-11-04T15:02:11.000Z", "updated_at": "2026-11-04T15:02:11.000Z"
  }
}
GET/api/v1/reels?restaurant={external_id}&limit=20

Your Reels, newest first. Leave out restaurant to get every restaurant's. limit is 1 to 100 (default 20). Every url is signed and lasts one hour (urls_expire_at); ask again for fresh ones.

{
  "reels": [
    {
      "id": "0b8e7c1d-...", "restaurant": "8", "job_id": "6f1c2a0e-...",
      "title": "The plancha that melts the big quesadilla", "idea": "...", "kind": "Cheese on the plancha",
      "dishes": ["Large Quesadilla"], "seconds": 11.2,
      "status": "delivered",
      "deliver_at": "2026-11-08T01:00:00.000Z", "delivered_at": "2026-11-08T01:00:12.000Z",
      "created_at": "2026-11-07T18:40:00.000Z",
      "cover": "https://...signed",
      "versions": [ { "index": 0, "register": "dembow", "url": "https://...full quality", "phone_url": "https://...smaller copy" } ],
      "chosen": 0,
      "gallery_url": null,
      "urls_expire_at": "2026-11-08T02:00:12.000Z"
    }
  ]
}
  • status: ready when the Reel is made but not dropped to you yet, delivered once it was.
  • versions: the same cut with different music. A platform's Reel has one; url is the full-quality file, phone_url a smaller copy for messaging apps.
  • gallery_url: the public "how Muse directed it" page, when the Reel is in the gallery. Otherwise null.

Jobs

GET/api/v1/jobs/{id}

Where a job is. When it is done, reel_id is the Reel it made.

statusMeaning
queuedWaiting for Muse to start.
runningMuse is on it: looking at the photos, pitching, filming takes, cutting. This takes a while.
waiting_ownerMuse asked the owner something it could not see in the photos (questions). Platform Reels are made without questions, so you should rarely see it.
doneThe Reel is made. See reel_id.
failedNo Reel this time; error says why. Most often: no dish with a usable photo in the kit.
cancelledStopped by Domi Reels.

Errors

Every error is JSON, { "error": "what went wrong" }, with its status:

  • 400 the request is not what the endpoint expects. 422 a field is out of range.
  • 401 no API key, or not a valid one.
  • 404 that restaurant, job or Reel is not yours, or does not exist.
  • 409 a conflict: two creates of the same restaurant at once (retry), or a Reel already being made for it.
  • 429 too many Reels in the last 24 hours. Every Reel costs real money, so the limit protects you from a loop in your code.
  • 500 our side. Retry in a moment.

When Reels arrive

  • Weekly Reels are made the day before delivery_day, from 9 AM in the restaurant's timezone, so Muse has the whole day.
  • A platform's Reel is dropped to you as soon as it is approved. When the owner sees it is up to you; delivery_hour is there so you can match it.
  • A Reel asked for with POST .../reels is dropped the moment it is done.

The contract: what your platform answers

Domi Reels does not need to know which ordering platform a restaurant uses. Any platform that answers these two calls gets Reels for its restaurants. Both are POST with JSON and the header x-reels-token: <your token>.

kit: domi-reels/kit@1

Before each Reel, Domi Reels asks for the restaurant's menu and photos.

POST {your kit url}
x-reels-token: <your token>

{ "restaurant_id": 8 }
{
  "contract": "domi-reels/kit@1",
  "platform": "doocado",
  "restaurant": { "id": 8, "name": "Pikalo - JP", "cuisine": "Empanadas, Sandwiches", "language": "en",
                  "city": "Jamaica Plain", "logo": "https://...", "brand_color": "" },
  "menu": [
    { "id": 676, "name": "Classico Dominicano", "description": "...", "price": 13.5,
      "photo": "https://...",
      "ai_note": "Dominican cheese is cheddar. The onion goes on the plancha, the tomato never does.",
      "addons": [{ "group": "Protein", "required": true, "options": ["Chicken", "Carnitas"] }] }
  ],
  "library": [
    { "id": 39, "photo": "https://...", "product_id": null, "dish": "Mangú Tripleta", "kind": "food" }
  ]
}
  • restaurant_id is your external_id for it (a number when it is all digits).
  • menu: what the restaurant sells, with its real photo when it has one ("" when not). ai_note is the owner's own note on the dish: Muse takes it as the owner's word, above what it sees in the photo. addons are the dish's option groups as the customer sees them ([] when none): Muse reads the sauce, the protein or the dip there before it asks anyone anything.
  • Menu ids must be numbers.
  • Photos keep coming: a menu photo with a new url is downloaded again, and every photo is rated by what is in it, so a new or better one takes over its dish on its own.
  • library: every photo the owner sent you: dishes, the griddle, the fryer, the dining room, the storefront. Muse logs each one. A dish with no menu photo takes the owner's library photo of it.
  • Only active restaurants. Nothing in the kit is private to anyone but the restaurant.

drop: domi-reels/drop@1

Two steps, so the video never travels inside JSON.

1. open. You answer with an upload url for each file; Domi Reels PUTs the files there.

POST {your drop url}
x-reels-token: <your token>

{ "action": "open", "restaurant_id": 8, "run": "8-0" }

=> { "upload": { "reel.mp4": "https://...", "cover.jpg": "https://..." } }

2. close. The Reel's words. You answer with its public links and the message for the owner, in the restaurant's language.

POST {your drop url}
x-reels-token: <your token>

{ "action": "close", "restaurant_id": 8, "run": "8-0",
  "title": "...", "owner_summary": "...",
  "dishes": [676], "ai_label": true, "cost": 1.42 }

=> { "video": "https://...", "cover": "https://...", "owner_message": "..." }

Then the owner gets it on your own channel (Doocado sends it on WhatsApp) and posts it.

Rules on the way out

  • A drop is a draft. Nothing in it publishes anything: the owner posts it from their own account.
  • Only a Reel Muse approved in its last look is dropped.
  • ai_label is always true: every Reel holds AI video, and the owner is told to turn on Instagram's AI label.
  • People in a Reel are made up, and only ever appear as hands. A Reel never presents anyone as a customer, a regular or a review.

Want your restaurants on Domi Reels?

We connect platforms one at a time. Ask the Domi Reels team for an API key.

See the Reels