Automations
Schedules, pulls, agent steps, and how to add an integration.
An automation is a schedule plus an ordered list of steps. Every step names a canvas, and the canvas is the context between steps: whatever a pull landed, the next agent step sees. There is no separate output node. Find them under Automations in the sidebar, or open one straight from an example on the automations page.
Schedules and steps
Schedules are daily, weekdays or weekly at a wall-clock time in your time zone, which is read from the browser when you create the automation. Two step types exist today:
- Pull asks an integration for designs and lands them on a canvas as frames. Meta Ads is the provider today. Pick the ad account and which ads: Active ads, Top 10 performers (by last week's results), or All ads, including paused ones.
- Agent queues a board card for one resident role on the canvas, the same card you would write on the Board yourself. The run counts as done once the card is queued; the agent's own progress shows on the canvas, and the card is billed to the automation's owner like any other.
Up to six steps per automation. A pull followed by an agent step is the common shape: the pull lands the material, the agent works on it. An automation with only agent steps is a scheduled review of whatever is already on the canvas.
Start from an example. The Automations page offers filled-in examples. Weekly creative
report is the pull-then-agent loop with the brief already written; you only pick the account and the canvas.
Friday UX review, Brand and accessibility check, Weekly copy pass and Nightly
polish are agent-only. Links of the form /automations?example=creative-report create the
automation from that example and open its editor.
Connect Meta
Open Integrations in the sidebar, or press Connect Meta on a pull step. Doop sends you to
Meta's consent screen asking for ads_read only, exchanges the code for a long-lived token, and lists the
ad accounts that token can read. The token is stored server-side and never appears in API responses; the
Integrations page shows the account name, the ad accounts and when the token expires.
- Connections are per user. Automations you own draw on your connection wherever they run.
- Disconnecting deletes the row. Automations that used it fail their next run with a Reconnect button.
- Long-lived tokens last about 60 days. When Meta rejects one, the run fails the same way, so the fix is always the same button.
-
Self-hosting: create a Meta app with the Marketing API product, set
META_APP_IDandMETA_APP_SECRET, and add<your origin>/api/integrations/meta/callbackas a valid OAuth redirect. Until the app passes Meta's review, only its admins and testers can connect.
What a pull lands
One frame per creative, in a four-column grid below whatever is already on the canvas, under a scoreboard frame:
- Creative frames show the image, headline, body and CTA as the ad runs, plus a strip with CTR, cost per result, spend, days running and a verdict. Dynamic-creative ads become one frame per image, carousels one frame per card. Images are copied into Doop's asset store, since Meta's URLs expire.
- The scoreboard ranks every pulled ad by last week's results, with account totals and arrows against the previous pull. It is written on the first pull and rewritten in place on every later one.
- Re-pulls update in place. A frame already on the canvas gets fresh numbers instead of a duplicate. Only the strip changes, so anything an agent or a person did to the rest of the frame survives.
Numbers cover the last 7 days, from Meta's ad-level insights. Results follow the objective the way Ads Manager does: purchases first, then leads, sign-ups, installs, adds to cart, landing page views, link clicks. Cost per result uses the same action. An ad that served nothing in the window shows No data. A pull fetches up to 200 ads.
Verdicts
Each ad gets one of four verdicts. They are computed from the numbers, not by a model, so they are the same every run:
- Scale: the top quarter by results (at least one ad), with a result in the window. On an account that reports no conversions at all, CTR decides instead.
- Refresh: running for 21 days or more, and either CTR below the account's median or frequency above 3.
- Watch: served, but neither of the above.
- No data: no impressions in the window.
The ranking is results first, then cost per result, then CTR. The scoreboard carries a one-line key so an agent reading it knows what each verdict asks for.
Writing the agent brief
The prompt is the brief. Say what to read first, what to do per verdict, and what shape the output should take. The example brief:
Read the scoreboard frame first: it ranks every ad by last week's results with a verdict per ad. For each ad marked "scale", make three variants as new frames next to it that keep what works (the offer, the visual, the tone) and change one thing each: a sharper hook, a different visual hierarchy, a shorter copy version. For each ad marked "refresh", make one new creative next to it that keeps the offer but changes the hook and the visual, so it reads as a new ad to people who have seen the old one. Leave "watch" ads alone. Keep the brand intact and put the copy for every new frame in its own headline, body and CTA so it can go straight into Ads Manager.
Any resident role can take the step: Doop for new creatives, the UX Lead for flows, the Copywriter for headlines, Brand and Accessibility for checks. One role per step; chain steps for a pipeline.
Runs and failures
The scheduler checks every minute for automations that are due and runs each one's steps in order. Every run is one line in the Runs tab: when it started, what happened, and where. A summary reads like 3 new creatives, 20 refreshed · scoreboard updated · card queued for Doop.
- Run now starts the same run the schedule would, on demand. A pull takes seconds; an agent step only queues.
- An automation runs one at a time. A tick that finds a run still going leaves it alone.
- A failed step stops the run and names the reason. When the fix is re-authorising Meta, the row shows Reconnect.
- An automation with a missing canvas or account is saved but not scheduled; the reason shows inline until it is complete.
- Switching an automation off clears its next run. Switching it back on schedules the next slot.
Build an integration
Doop is open source, and Meta is the reference integration. An integration is a server module that connects an account, lists what the user can pull from, and lands frames on a canvas. Here is where the Meta one lives, which is also the map for adding another:
-
server/meta.ts: the whole provider. OAuth state signing, the code exchange,fetchIdentityfor the account list, the Graph API calls that fetch ads and insights, the ranking, andpullCreatives, which turns the result into frames. -
server/integrations.ts: the routes under/api/integrations. Status, start, callback, disconnect. The callback verifies the signed state belongs to the signed-in user before it trusts the code. -
server/db/schema.ts: theintegrationstable, one row per user and provider, holding the token and the account list. Theprovidercolumn is what your integration adds a value to. -
shared/automations.ts: the step model shared by server and client.PullStepnames the provider, the account and the filter;normalizeStepsvalidates it. -
server/automations.ts: the runner.runPullresolves the connection and calls the provider; a thrownMetaAuthErrorbecomes the Reconnect failure. -
src/pages/Integrations.tsxand the pull node insrc/pages/AutomationEditor.tsx: the connect button and the account picker.
Three conventions are worth keeping whatever you connect:
- Marker meta on every frame. A pulled frame carries
<meta name="doop-meta-ad" content="<id>">. The next pull finds it and updates in place instead of adding a duplicate. Use your own marker name. - Copy remote images into the asset store. Signed URLs expire;
keepImagein the Meta provider shows the guarded fetch that keeps the frame rendering. - Read scopes first. Every integration so far only reads. A push step that writes back (paused ads, a pull request) is planned as its own step type, with its own explicit consent.
Today those touch points are wired by hand; a single provider interface that collapses them into one file is on the roadmap, and contributions that move toward it are welcome. Read CONTRIBUTING.md first, open an issue naming the service and whether it pulls, pushes or both, and start from the Meta files above. The other sources that already land frames, the GitHub import, the website import and the sync snippet, follow the same marker convention and are worth reading alongside.