@adsuploader/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/SKILL.md +517 -0
- package/dist/cli.cjs +5971 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Ads Uploader CLI
|
|
2
|
+
|
|
3
|
+
Create and manage Meta ads from the command line. Same pipeline as the web app.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @adsuploader/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18 or later.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ads login # authenticate via browser
|
|
17
|
+
ads accounts # list ad accounts
|
|
18
|
+
ads account act_123456 # set default account
|
|
19
|
+
|
|
20
|
+
ads upload hero.jpg banner.mp4 # upload media
|
|
21
|
+
ads create:preview spec.json # preview what would be created
|
|
22
|
+
ads create spec.json # create ads
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Documentation
|
|
26
|
+
|
|
27
|
+
Full documentation including the spec file reference, campaign structure, text configuration, and all available options is at [adsuploader.com/docs/ad-configuration/cli](https://adsuploader.com/docs/ad-configuration/cli).
|
|
28
|
+
|
|
29
|
+
## Using with AI
|
|
30
|
+
|
|
31
|
+
This package includes a `SKILL.md` file that describes every command and option for AI agents (Claude Code, Cursor, etc.). Point your AI tool at `node_modules/@adsuploader/cli/SKILL.md` to get started.
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
| Command | Description |
|
|
36
|
+
|---------|-------------|
|
|
37
|
+
| `ads login` | Authenticate via browser |
|
|
38
|
+
| `ads accounts` | List ad accounts |
|
|
39
|
+
| `ads account <id>` | Set default account |
|
|
40
|
+
| `ads campaigns` | List campaigns |
|
|
41
|
+
| `ads campaign <id>` | Show ad sets in a campaign |
|
|
42
|
+
| `ads adset <id>` | Show ads in an ad set |
|
|
43
|
+
| `ads ad <id>` | Ad details and creative |
|
|
44
|
+
| `ads presets` | List saved presets |
|
|
45
|
+
| `ads upload <files...>` | Upload images and videos |
|
|
46
|
+
| `ads uploads` | List recent upload batches |
|
|
47
|
+
| `ads create spec.json` | Create ads from spec |
|
|
48
|
+
| `ads create:preview spec.json` | Dry run |
|
|
49
|
+
| `ads create:interactive` | Guided wizard |
|
|
50
|
+
| `ads jobs <id>` | Check job status |
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ads
|
|
3
|
+
description: Create and manage Facebook/Meta ads using the Ads Uploader CLI. Upload media, preview configurations, create ads, browse campaigns and ad sets.
|
|
4
|
+
homepage: https://adsuploader.com
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @adsuploader/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Login (opens browser OAuth flow)
|
|
17
|
+
ads login
|
|
18
|
+
|
|
19
|
+
# 2. List ad accounts
|
|
20
|
+
ads accounts
|
|
21
|
+
|
|
22
|
+
# 3. Set default account
|
|
23
|
+
ads account act_123456789
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Credentials are stored at `~/.config/adsuploader/credentials.json`. Check current auth:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ads whoami
|
|
30
|
+
ads config
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Core Workflow
|
|
36
|
+
|
|
37
|
+
Every ad creation follows three steps:
|
|
38
|
+
|
|
39
|
+
### 1. Upload media
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
ads upload hero.jpg banner.mp4
|
|
43
|
+
ads upload ./my-creatives/ # entire directory
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Returns a **batch ID** (e.g. `batch_abc123`) — you'll need this for the spec file. Files are uploaded directly to the selected ad account's Facebook media library, so the batch ID is tied to that account.
|
|
47
|
+
|
|
48
|
+
### 2. Preview (dry run)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ads create:preview spec.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Shows exactly what would be created without touching Facebook. **Always preview first.**
|
|
55
|
+
|
|
56
|
+
### 3. Create ads
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ads create spec.json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Ads are created **ACTIVE** by default. Use `--status PAUSED` to create them paused. Returns a job ID for progress tracking.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Command Reference
|
|
67
|
+
|
|
68
|
+
### Auth
|
|
69
|
+
| Command | Description |
|
|
70
|
+
|---------|-------------|
|
|
71
|
+
| `login` | Authenticate via browser |
|
|
72
|
+
| `logout` | Clear credentials |
|
|
73
|
+
| `whoami` | Show current user |
|
|
74
|
+
| `config` | Show configuration |
|
|
75
|
+
|
|
76
|
+
### Browsing
|
|
77
|
+
| Command | Description |
|
|
78
|
+
|---------|-------------|
|
|
79
|
+
| `accounts` | List ad accounts |
|
|
80
|
+
| `account <id>` | Set default account |
|
|
81
|
+
| `campaigns` | List active campaigns (`--status all` for all, `--search <text>` to filter) |
|
|
82
|
+
| `campaign <id>` | Show ad sets in a campaign |
|
|
83
|
+
| `adsets --campaign <id>` | List ad sets (supports `--search`, `--status`) |
|
|
84
|
+
| `adset <adSetId>` | Show ads in an ad set |
|
|
85
|
+
| `ad <adId>` | Ad details + creative config |
|
|
86
|
+
| `presets` | List saved API presets (or `presets <id>` for details) |
|
|
87
|
+
| `text-presets` | List saved text presets (or `text-presets <id>` for details) |
|
|
88
|
+
| `uploads` | List recent upload batches (or `uploads <batchId>` for details) |
|
|
89
|
+
|
|
90
|
+
### Actions
|
|
91
|
+
| Command | Description |
|
|
92
|
+
|---------|-------------|
|
|
93
|
+
| `upload <files...>` | Upload images/videos |
|
|
94
|
+
| `create spec.json` | Create ads from spec |
|
|
95
|
+
| `create:preview spec.json` | Dry run — show what would be created |
|
|
96
|
+
| `create:interactive` | Guided wizard (accepts all create flags) |
|
|
97
|
+
|
|
98
|
+
### Jobs
|
|
99
|
+
| Command | Description |
|
|
100
|
+
|---------|-------------|
|
|
101
|
+
| `jobs <jobId>` | Check job status |
|
|
102
|
+
| `jobs <jobId> --follow` | Follow progress in real-time |
|
|
103
|
+
| `jobs cancel <jobId>` | Cancel a running job |
|
|
104
|
+
|
|
105
|
+
### Create Flags
|
|
106
|
+
| Flag | Description |
|
|
107
|
+
|------|-------------|
|
|
108
|
+
| `--account <id>` | Override default ad account |
|
|
109
|
+
| `--preset <id>` | API preset ID (alternative to spec file) |
|
|
110
|
+
| `--text-preset <id>` | Text preset ID |
|
|
111
|
+
| `--copy-from <adId>` | Ad ID to copy settings from |
|
|
112
|
+
| `--upload <batchId>` | Upload batch ID |
|
|
113
|
+
| `--status <PAUSED\|ACTIVE>` | Set ad status (default: ACTIVE) |
|
|
114
|
+
| `--pause-at <level>` | Pause level: `ad` (default), `adSet`, or `campaign` |
|
|
115
|
+
| `--daily-budget <amount>` | Override daily budget (currency units) |
|
|
116
|
+
| `--bid-amount <amount>` | Override bid/cost cap (currency units) |
|
|
117
|
+
| `--text-file <path>` | Load text config from a JSON file |
|
|
118
|
+
|
|
119
|
+
### Common Flags
|
|
120
|
+
| Flag | Description |
|
|
121
|
+
|------|-------------|
|
|
122
|
+
| `--account <id>` | Override default ad account |
|
|
123
|
+
| `--json` | Raw JSON output (available on most commands) |
|
|
124
|
+
|
|
125
|
+
### Do not use `--json`
|
|
126
|
+
|
|
127
|
+
The `--json` flag is for shell scripts piping to `jq`. **Never use it** — the human-readable output already contains all the information you need (batch IDs, job progress, results). For `create`, `--json` replaces the live progress log with raw NDJSON poll dumps which are unreadable.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Spec File Format
|
|
132
|
+
|
|
133
|
+
The JSON spec controls ad creation. Only `uploadId` and a template source are required.
|
|
134
|
+
|
|
135
|
+
### Minimal Spec
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"adPresetId": "preset_id_here",
|
|
140
|
+
"uploadId": "batch_abc123"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Template Source (pick one)
|
|
145
|
+
|
|
146
|
+
| Field | Description |
|
|
147
|
+
|-------|-------------|
|
|
148
|
+
| `adPresetId` | Saved API preset ID (locks campaign, ad set, ad config) |
|
|
149
|
+
| `copyFromAd` | Facebook ad ID to copy settings from |
|
|
150
|
+
|
|
151
|
+
When using `copyFromAd`, provide the upload batch and optionally campaign/ad set:
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"copyFromAd": "120233848667930472",
|
|
155
|
+
"uploadId": "batch_abc123",
|
|
156
|
+
"campaign": { "id": "120233848666410472" },
|
|
157
|
+
"adSet": { "id": "120233848666620472" }
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Campaign Structure
|
|
162
|
+
|
|
163
|
+
**Single campaign** (default): ads go into the template ad's campaign, or a new campaign if `campaign.name` is provided.
|
|
164
|
+
|
|
165
|
+
**Multi-campaign modes** — use `campaign.mode` with a `campaigns` array:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"campaign": {
|
|
170
|
+
"mode": "duplicate",
|
|
171
|
+
"campaigns": [
|
|
172
|
+
{ "name": "Campaign A" },
|
|
173
|
+
{ "name": "Campaign B" }
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
| Mode | Behavior |
|
|
180
|
+
|------|----------|
|
|
181
|
+
| `"single"` | Default — one campaign |
|
|
182
|
+
| `"duplicate"` | All media duplicated into each campaign |
|
|
183
|
+
| `"split"` | Media split across campaigns |
|
|
184
|
+
|
|
185
|
+
### Ad Set Modes
|
|
186
|
+
|
|
187
|
+
**Single ad set** (default):
|
|
188
|
+
```json
|
|
189
|
+
{ "adSet": { "name": "My Ad Set" } }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Existing ad set:**
|
|
193
|
+
```json
|
|
194
|
+
{ "adSet": { "id": "120233848666620472" } }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Per upload** (one ad set per file):
|
|
198
|
+
```json
|
|
199
|
+
{ "adSet": { "mode": "perUpload" } }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Auto group** (split evenly):
|
|
203
|
+
```json
|
|
204
|
+
{ "adSet": { "mode": "autoGroup", "adsPerAdSet": 5 } }
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Custom groups:**
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"adSet": {
|
|
211
|
+
"groups": [
|
|
212
|
+
{ "name": "Images", "media": ["hero.jpg", "banner.jpg"] },
|
|
213
|
+
{ "name": "Video", "media": ["promo.mp4"] }
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Ad set naming pattern** (for multi-ad-set modes):
|
|
220
|
+
```json
|
|
221
|
+
{ "adSet": { "mode": "perUpload", "namePattern": "Ad Set {index:01}" } }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Variant grouping** (group ads by variation identifier into the same ad set):
|
|
225
|
+
```json
|
|
226
|
+
{ "adSet": { "mode": "autoGroup", "groupVariations": true, "variationIdentifier": "-" } }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Budget / Bid Override
|
|
230
|
+
|
|
231
|
+
Values in account currency units (e.g. `50` = $50). Only one at a time.
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{ "adSet": { "dailyBudget": 50 } }
|
|
235
|
+
{ "adSet": { "bidAmount": 5 } }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Also available as CLI flags: `--daily-budget 50` or `--bid-amount 5`.
|
|
239
|
+
|
|
240
|
+
### Text Configuration
|
|
241
|
+
|
|
242
|
+
**Common text** (same for all ads):
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"texts": {
|
|
246
|
+
"common": {
|
|
247
|
+
"headlines": ["Headline 1", "Headline 2"],
|
|
248
|
+
"bodies": ["Primary text"],
|
|
249
|
+
"descriptions": ["Description"]
|
|
250
|
+
},
|
|
251
|
+
"strategy": "flexible"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Per-ad text** (unique per file):
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"texts": {
|
|
260
|
+
"perAd": {
|
|
261
|
+
"hero.jpg": {
|
|
262
|
+
"headlines": ["Hero Headline"],
|
|
263
|
+
"bodies": ["Hero copy"],
|
|
264
|
+
"descriptions": ["Hero desc"],
|
|
265
|
+
"cta": "LEARN_MORE",
|
|
266
|
+
"link": "https://example.com/hero",
|
|
267
|
+
"urlTags": "utm_content=hero"
|
|
268
|
+
},
|
|
269
|
+
"banner.jpg": {
|
|
270
|
+
"headlines": ["Banner Headline"],
|
|
271
|
+
"bodies": ["Banner copy"]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Per-ad keys are **filenames** (not full paths). Unspecified fields inherit from the template ad.
|
|
279
|
+
|
|
280
|
+
**Text preset:**
|
|
281
|
+
```json
|
|
282
|
+
{ "textPresetId": "preset_id_here" }
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Cannot combine `textPresetId` with `texts`.
|
|
286
|
+
|
|
287
|
+
**Strategy options:**
|
|
288
|
+
- `"flexible"` (default) — Meta optimizes across text variations (multiple headlines/bodies become options Facebook mixes)
|
|
289
|
+
- `"separate"` — each text combination becomes a separate ad
|
|
290
|
+
|
|
291
|
+
Per-ad entries support: `headlines`, `bodies`, `descriptions`, `cta`, `link`, `displayUrl`, `urlTags`. Unspecified fields inherit from the template ad.
|
|
292
|
+
|
|
293
|
+
### CTA and Links
|
|
294
|
+
|
|
295
|
+
Top-level CTA applies to all ads (overridden by per-ad CTA):
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"cta": {
|
|
299
|
+
"type": "SHOP_NOW",
|
|
300
|
+
"link": "https://example.com",
|
|
301
|
+
"displayUrl": "example.com"
|
|
302
|
+
},
|
|
303
|
+
"urlTags": "utm_source=facebook&utm_medium=paid"
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Standard CTA types:** `LEARN_MORE`, `SHOP_NOW`, `SIGN_UP`, `SUBSCRIBE`, `GET_OFFER`, `CONTACT_US`, `DOWNLOAD`, `ORDER_NOW`, `BUY_NOW`, `BOOK_NOW`, `APPLY_NOW`, `GET_QUOTE`, `GET_IN_TOUCH`, `WATCH_MORE`
|
|
308
|
+
|
|
309
|
+
**Objective-specific CTAs** (inherited from template ad — do not set manually):
|
|
310
|
+
|
|
311
|
+
| CTA | Required Objective |
|
|
312
|
+
|-----|-------------------|
|
|
313
|
+
| `MESSAGE_PAGE` | Messenger destination |
|
|
314
|
+
| `WHATSAPP_MESSAGE` | WhatsApp destination |
|
|
315
|
+
| `INSTAGRAM_MESSAGE` | Instagram DM destination |
|
|
316
|
+
| `CALL_NOW` | Call campaign |
|
|
317
|
+
|
|
318
|
+
### Creative Enhancements
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{ "creativeEnhancements": "none" }
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
| Value | Effect |
|
|
325
|
+
|-------|--------|
|
|
326
|
+
| `"metaDefaults"` | Let Meta decide (default if omitted) |
|
|
327
|
+
| `"all"` | All features on |
|
|
328
|
+
| `"none"` | All features off |
|
|
329
|
+
| `["text_translation", "image_touchups"]` | Listed features on, rest off |
|
|
330
|
+
|
|
331
|
+
Available features: `text_translation`, `inline_comment`, `enhance_cta`, `text_optimizations`, `reveal_details_over_time`, `image_brightness_and_contrast`, `image_touchups`, `video_auto_crop`, `video_filtering`, `image_animation`, `image_templates`, `adapt_to_placement`, `product_extensions`, `description_automation`, `add_text_overlay`, `music`, `carousel_to_video`, `carousel_dynamic_description`, `multi_share_end_card`, `multi_share_optimized`
|
|
332
|
+
|
|
333
|
+
When cherry-picking features, only list ones relevant to the media type. `video_auto_crop` and `video_filtering` only apply to video ads. `carousel_to_video`, `carousel_dynamic_description`, `multi_share_end_card`, and `multi_share_optimized` only apply to carousel ads.
|
|
334
|
+
|
|
335
|
+
### Carousel Ads
|
|
336
|
+
|
|
337
|
+
Group uploaded files into carousel ads with per-card text:
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"carousel": [
|
|
342
|
+
{
|
|
343
|
+
"name": "My Carousel",
|
|
344
|
+
"cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
|
|
345
|
+
"cardTexts": [
|
|
346
|
+
{ "headline": "Slide 1", "description": "First card", "link": "https://example.com/1" },
|
|
347
|
+
{ "headline": "Slide 2", "description": "Second card", "link": "https://example.com/2" }
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Cards must reference filenames from the upload batch. Minimum 2 cards per carousel. Files claimed by a carousel are removed from the standard ad list.
|
|
355
|
+
|
|
356
|
+
### Flexible Ads
|
|
357
|
+
|
|
358
|
+
Group multiple assets into a single flexible ad (Meta picks the best asset per placement):
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
{
|
|
362
|
+
"flexible": [
|
|
363
|
+
{
|
|
364
|
+
"name": "Multi-Asset Ad",
|
|
365
|
+
"assets": ["hero.jpg", "promo.mp4", "banner.jpg"]
|
|
366
|
+
}
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Minimum 2 assets per group. Files claimed by a flexible group are removed from the standard ad list.
|
|
372
|
+
|
|
373
|
+
### Ad Naming
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{ "adNamePattern": "{filename} - {date}" }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Placeholders: `{filename}`, `{index:01}`, `{variation}`, `{campaign}`, `{date}`, `{date:short}`, `{timestamp}`
|
|
380
|
+
|
|
381
|
+
### Options
|
|
382
|
+
|
|
383
|
+
```json
|
|
384
|
+
{
|
|
385
|
+
"options": {
|
|
386
|
+
"status": "PAUSED",
|
|
387
|
+
"pauseAt": "ad",
|
|
388
|
+
"schedule": {
|
|
389
|
+
"startTime": "2026-04-01T09:00:00",
|
|
390
|
+
"endTime": "2026-04-30T23:59:59"
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Field | Values | Description |
|
|
397
|
+
|-------|--------|-------------|
|
|
398
|
+
| `status` | `"PAUSED"`, `"ACTIVE"` | Ad launch status (default: ACTIVE) |
|
|
399
|
+
| `pauseAt` | `"ad"`, `"adSet"`, `"campaign"` | Which level to pause (default: ad) |
|
|
400
|
+
| `schedule.startTime` | ISO 8601 string | Scheduled start (uses ad account timezone) |
|
|
401
|
+
| `schedule.endTime` | ISO 8601 string | Scheduled end (optional) |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Upload & Variant Detection
|
|
406
|
+
|
|
407
|
+
Variant groups are detected automatically from filename conventions:
|
|
408
|
+
|
|
409
|
+
**Ratio suffixes:**
|
|
410
|
+
`hero_4x5.jpg` + `hero_9x16.jpg` + `hero_16x9.jpg` → grouped as one variant ad
|
|
411
|
+
|
|
412
|
+
**Word suffixes:**
|
|
413
|
+
`hero.jpg` + `hero_vertical.jpg` + `hero_horizontal.jpg` → grouped as one variant ad
|
|
414
|
+
|
|
415
|
+
Both `_` and `-` delimiters work. A file without a ratio suffix (e.g. `hero.jpg`) only groups with `_vertical`/`_horizontal` variants.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Common Patterns
|
|
420
|
+
|
|
421
|
+
### Simple upload + create with preset
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
# Upload
|
|
425
|
+
ads upload ./creatives/hero.jpg ./creatives/banner.jpg
|
|
426
|
+
# Note the batchId from output (printed as "Batch: batch_xxx")
|
|
427
|
+
|
|
428
|
+
# Write spec
|
|
429
|
+
cat > /tmp/spec.json << 'EOF'
|
|
430
|
+
{
|
|
431
|
+
"adPresetId": "PRESET_ID",
|
|
432
|
+
"uploadId": "BATCH_ID"
|
|
433
|
+
}
|
|
434
|
+
EOF
|
|
435
|
+
|
|
436
|
+
# Preview, then create
|
|
437
|
+
ads create:preview /tmp/spec.json
|
|
438
|
+
ads create /tmp/spec.json
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Per-ad text (unique copy per file)
|
|
442
|
+
|
|
443
|
+
```json
|
|
444
|
+
{
|
|
445
|
+
"adPresetId": "PRESET_ID",
|
|
446
|
+
"uploadId": "BATCH_ID",
|
|
447
|
+
"texts": {
|
|
448
|
+
"perAd": {
|
|
449
|
+
"hero.jpg": {
|
|
450
|
+
"headlines": ["Summer Sale Now On"],
|
|
451
|
+
"bodies": ["Save up to 50% on all items"],
|
|
452
|
+
"cta": "SHOP_NOW",
|
|
453
|
+
"link": "https://example.com/summer"
|
|
454
|
+
},
|
|
455
|
+
"banner.jpg": {
|
|
456
|
+
"headlines": ["New Collection Available"],
|
|
457
|
+
"bodies": ["Browse our latest styles"],
|
|
458
|
+
"cta": "LEARN_MORE",
|
|
459
|
+
"link": "https://example.com/new"
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
"options": { "status": "PAUSED" }
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Auto-group into multiple ad sets
|
|
468
|
+
|
|
469
|
+
```json
|
|
470
|
+
{
|
|
471
|
+
"adPresetId": "PRESET_ID",
|
|
472
|
+
"uploadId": "BATCH_ID",
|
|
473
|
+
"adSet": { "mode": "autoGroup", "adsPerAdSet": 3 },
|
|
474
|
+
"options": { "status": "PAUSED" }
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Copy from existing ad
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Find the ad to copy from
|
|
482
|
+
ads campaigns
|
|
483
|
+
ads campaign 120233848666410472 # shows ad sets
|
|
484
|
+
ads adset 120233848666620472 # shows ads
|
|
485
|
+
ads ad 120233848667930472 # shows ad details
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{
|
|
490
|
+
"copyFromAd": "120233848667930472",
|
|
491
|
+
"campaign": { "id": "120233848666410472" },
|
|
492
|
+
"uploadId": "BATCH_ID",
|
|
493
|
+
"options": { "status": "PAUSED" }
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Follow job progress
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
ads create spec.json
|
|
501
|
+
# Shows live progress automatically; to check a job later:
|
|
502
|
+
ads jobs JOB_ID --follow
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Critical Gotchas
|
|
508
|
+
|
|
509
|
+
1. **Always preview first** — `create:preview` catches config errors before touching Facebook
|
|
510
|
+
2. **Ads are ACTIVE by default** — use `--status PAUSED` or `"status": "PAUSED"` in spec to create them paused
|
|
511
|
+
3. **`uploadId` comes from upload output** — it's the batch ID returned by `ads upload`
|
|
512
|
+
4. **Uploads are tied to an ad account** — files are uploaded directly to the selected account's Facebook media library. The batch ID can only be used with the same account
|
|
513
|
+
5. **`copyFromAd` needs `uploadId`** — you must provide the upload batch. Optionally provide `campaign.id` and `adSet.id` to control placement
|
|
514
|
+
6. **Per-ad text keys are filenames** — use `"hero.jpg"`, not `"/path/to/hero.jpg"`
|
|
515
|
+
7. **`textPresetId` and `texts` are mutually exclusive** — use one or the other
|
|
516
|
+
8. **Objective-specific CTAs** (`MESSAGE_PAGE`, `WHATSAPP_MESSAGE`, etc.) are inherited from the template — don't set them manually
|
|
517
|
+
9. **Do not use `--json`** — it's for shell scripts, not interactive use. The human-readable output has everything you need
|