> For the complete documentation index, see [llms.txt](https://series-1.gitbook.io/rundot-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://series-1.gitbook.io/rundot-docs/v5.22.0/readme/marketing-your-game.md).

# Marketing Your Game (BETA)

## Marketing Your Game (BETA)

Run paid ad campaigns for your game straight from the `rundot` CLI. You write a short prompt, RUN.game generates the ad creatives, copy, and screenshots for you, and a campaign is submitted to Meta (Facebook / Instagram) to drive new players to your game.

{% hint style="info" %}
This is **paid user acquisition** — *spending* to bring new players to your game. To *earn* revenue by showing ads inside your game, see the [Ad Monetization API](/rundot-docs/v5.22.0/readme/ads.md).
{% endhint %}

{% hint style="warning" %}
**Beta feature.** Marketing commands are hidden by default. Enable them for your shell session before you start:

```bash
export RUNDOT_BETA_FEATURES=1
```

Without this set, `rundot marketing` commands won't appear in `--help`.
{% endhint %}

### How it works

You build a campaign locally, RUN.game generates the assets, and you submit it. Submitting **does not start spending money** — every campaign is reviewed by the RUN.game team and only goes live once it's approved ("flighted").

```
rundot marketing prepare   →  scaffold a campaign + write its prompt
rundot marketing generate  →  RUN.game generates ad images (AI)
rundot marketing copy      →  RUN.game writes headline / body options (AI)
rundot marketing submit    →  upload + submit for review (created PAUSED)
        │
        ▼
  RUN.game review  →  approved campaigns are flighted (go live) and start spending
        │
        ▼
rundot marketing stats     →  track spend, installs, ROAS, and more
```

All commands run from your game's project directory and use the same `game.config.prod.json` as `rundot deploy`. Add `--game-id` to target a specific game.

### 1. Prepare a campaign

```bash
rundot marketing prepare --name spring-push
```

This creates a local campaign folder under `.rundot/marketing/spring-push/`:

```
.rundot/marketing/spring-push/
  campaign.json          # the campaign manifest (prompts, targeting, etc.)
  ad-creative/           # AI-generated ad images
  screenshots/           # AI-generated or hand-uploaded screenshots
  feature-graphic/       # optional feature graphic
  video/                 # optional hand-uploaded video
  references/            # reference images that guide AI generation
```

Useful options:

| Option                                  | Purpose                                                      |
| --------------------------------------- | ------------------------------------------------------------ |
| `--platforms web,ios,android`           | Which platforms to advertise on (default: all three).        |
| `--countries US,BR,MX`                  | Target specific countries (2-letter ISO codes). Default: US. |
| `--age-min 21` / `--age-max 45`         | Target an age range (13–65). Default: 18–65.                 |
| `--prompt-ad-creative "..."`            | Override the ad-image prompt inline.                         |
| `--prompt-ad-creative-file ./prompt.md` | Provide the prompt from a file.                              |
| `--override`                            | Overwrite an existing campaign of the same name.             |

Prompts default to built-in templates that fill in your game's name and description automatically. Edit `campaign.json` (or use the `--prompt-*` flags) to customize them.

### 2. Generate creatives

```bash
rundot marketing generate --name spring-push
```

RUN.game generates the ad images server-side, resizes them to spec, and downloads them into your campaign folder.

| Option                                     | Purpose                                                                                                  |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| `--kind ad-creative`                       | Generate only one asset kind (`ad-creative`, `screenshots`, or `feature-graphic`). Omit to generate all. |
| `--variants 4`                             | How many variants to generate (capped per asset kind).                                                   |
| `--reference ./logo.png,https://…/art.png` | Reference images (local files or HTTPS URLs) to guide style.                                             |
| `--regen ad-creative/0.png`                | Regenerate a single asset.                                                                               |
| `--new-seed`                               | Use a fresh random seed when regenerating.                                                               |
| `--force`                                  | Overwrite existing assets.                                                                               |

You can also hand-place your own images, screenshots, or a video into the folders instead of generating them.

### 3. Generate ad copy (optional)

```bash
rundot marketing copy --name spring-push --count 5
```

Generates headline and primary-text options for the ad. Meta automatically tests these against each other and favors the best performers.

### 4. Submit for review

```bash
rundot marketing submit --name spring-push
```

This uploads your assets and submits the campaign. The campaign is created **paused** — it is **not spending yet**. The RUN.game team reviews it, and once approved it's flighted (goes live).

You'll see the campaign move through these states (visible via `rundot marketing status`):

| Status              | Meaning                                               |
| ------------------- | ----------------------------------------------------- |
| `pending-review`    | Submitted and waiting for review. Not spending.       |
| `flighted`          | Approved and live. Spending against its daily budget. |
| `rejected`          | Not approved. Not spending.                           |
| `paused-by-creator` | You paused it. Not spending.                          |
| `cancelled`         | Permanently stopped.                                  |

### Reviewing performance

Check a campaign's status and budgets:

```bash
rundot marketing status --name spring-push
```

Pull performance metrics (spend, impressions, clicks, CTR, installs, CPI, revenue, ROAS, and more):

```bash
# Last 30 days as a table (default)
rundot marketing stats --name spring-push

# As CSV for a spreadsheet
rundot marketing stats --name spring-push --days 30 --format csv > spring-push.csv
```

{% hint style="info" %}
Some metrics (reach, CTR, conversions) show `—` in a table or a blank cell in CSV when the ad network didn't report them. That means **no data**, not zero.
{% endhint %}

List all your campaigns with their latest numbers:

```bash
rundot marketing list
```

### Managing a live campaign

```bash
# Change the daily budget (USD/day per platform)
rundot marketing budget --name spring-push --platforms ios,android --daily 200

# Pause spending (you can resume later)
rundot marketing pause --name spring-push
rundot marketing resume --name spring-push

# Permanently stop a campaign
rundot marketing cancel --name spring-push
```

{% hint style="warning" %}
Always change budgets through the CLI (or the controls the RUN.game team uses), **not** directly in Meta Ads Manager. Out-of-band edits in Ads Manager are detected automatically and will pause the campaign.
{% endhint %}

### Paying for ad spend

Campaigns are funded from your RUN.game **credits**. A few things to know:

* A campaign never spends until it's approved and flighted by the RUN.game team.
* Ad spend is drawn from your prepaid credit balance — the platform never fronts your ad cost. If your balance can't cover the next day's budget, the campaign is paused automatically.
* Pausing or cancelling a campaign refunds the unspent portion of its prepaid budget.

If a budget change is blocked for insufficient credits, top up your balance and try again.

### Asset specifications

Generated and hand-uploaded assets must meet these specs (the CLI validates them before submit):

#### Images

| Kind            | Aspect Ratio | Size (px)   | Format | Max File Size | Max Variants |
| --------------- | ------------ | ----------- | ------ | ------------- | ------------ |
| Ad creative     | 9:16         | 1080 × 1920 | PNG    | 8 MB          | 4            |
| Screenshots     | 9:16         | 1242 × 2208 | PNG    | 8 MB          | 4            |
| Feature graphic | 21:9         | 1024 × 500  | PNG    | 8 MB          | 1            |

Screenshots require a minimum of **3** at submit time. Images are resized with a "contain" fit and a blurred background fill, so they're never cropped.

#### Video (optional)

| Property      | Value         |
| ------------- | ------------- |
| Format        | MP4           |
| Duration      | 15–30 seconds |
| Max file size | 100 MB        |

Video is hand-uploaded (not AI-generated). The first ad creative is used as its thumbnail.

#### Reference images

| Property          | Value                                  |
| ----------------- | -------------------------------------- |
| Max count         | 4                                      |
| Max size per file | 5 MB                                   |
| Formats           | PNG, JPEG, WebP                        |
| URLs              | HTTPS URLs accepted (no upload needed) |

### Content moderation

All campaigns are moderated before they reach the ad network. Headlines, body copy, and prompts are checked for profanity and policy violations, and generated/uploaded images and video are scanned. Anything that fails moderation rejects the submission with a descriptive error, so keep creatives and copy advertising-policy compliant.

### Command reference

All commands live under `rundot marketing` and require `RUNDOT_BETA_FEATURES=1`.

| Command     | Purpose                                                 |
| ----------- | ------------------------------------------------------- |
| `prepare`   | Scaffold a campaign and write `campaign.json`.          |
| `generate`  | Generate ad images (AI).                                |
| `copy`      | Generate headline / body-copy options (AI).             |
| `composite` | Overlay a logo or image onto a generated asset.         |
| `refs`      | Build the reference palette for generation.             |
| `tips`      | Print prompt-writing guidance and asset requirements.   |
| `submit`    | Upload assets and submit the campaign for review.       |
| `status`    | Show a campaign's status, budgets, and IDs.             |
| `preview`   | Render a local preview of a prepared campaign.          |
| `stats`     | Show performance metrics.                               |
| `list`      | List all campaigns with their latest snapshot.          |
| `budget`    | Adjust a live campaign's daily budget.                  |
| `pause`     | Pause a live campaign (refunds unspent budget).         |
| `resume`    | Resume a paused campaign.                               |
| `cancel`    | Cancel a campaign permanently (refunds unspent budget). |

### How-tos

#### Launch a campaign end-to-end

```bash
export RUNDOT_BETA_FEATURES=1
rundot marketing prepare  --name spring-push
# edit prompts in campaign.json if you like, then:
rundot marketing generate --name spring-push
rundot marketing copy     --name spring-push --count 5
rundot marketing submit   --name spring-push
```

The campaign is submitted **paused** and starts spending only after the RUN.game team approves and flights it.

#### Target specific countries and ages

```bash
rundot marketing prepare  --name brazil-mx --countries BR,MX --age-min 21 --age-max 45
rundot marketing generate --name brazil-mx
rundot marketing submit   --name brazil-mx
```

Omit any flag to keep the default for that dimension (default audience: US, ages 18–65).

#### Read performance as a spreadsheet

```bash
rundot marketing stats --name spring-push --days 30 --format csv > spring-push.csv
```

## Share an update (launch packet)

`rundot marketing` is *paid* user acquisition. A **launch packet** is the *organic* counterpart: when you ship an update, RUN.game writes platform-ready posts (X, Reddit, TikTok, Instagram, Discord), mints tracked links so plays from your posts are attributed back to you, and gives you a posting checklist.

{% hint style="info" %}
**Paid vs. organic.** Use `rundot marketing` to *spend* on ads. Use `rundot socials` to share an update to your own audience for free. They're independent — use either or both.
{% endhint %}

{% hint style="warning" %}
**Beta feature.** Like `marketing`, `socials` commands are hidden until you enable beta features for your shell session:

```bash
export RUNDOT_BETA_FEATURES=1
```

{% endhint %}

### How it works

```
rundot socials prepare       →  RUN.game writes drafts + tracked links for each platform
rundot socials status        →  see the posting checklist (what's posted, what's pending)
rundot socials open <plat>   →  print the caption + a prefilled composer URL to post
rundot socials mark-posted   →  record the URL once you've posted (so @RUN can amplify it)
```

Discord is **auto-posted** server-side (if you've configured a webhook). Every other platform is **composer-assisted**: RUN.game gives you the caption and a prefilled composer link, and you click publish.

All commands run from your game's project directory and use the same game config as `rundot deploy`. Add `--game-id <id>` to target a specific game.

### 1. (One-time) configure your social profile

Your social profile is **per-creator, not per-game** — set it once and it applies to every game you publish. It customizes the generated copy and enables Discord auto-posting. It's optional — `prepare` works without it — but setting at least a Discord webhook is recommended.

```bash
rundot socials profile set \
  --discord-webhook "https://discord.com/api/webhooks/…" \
  --tone "hyped but humble" \
  --hashtags "indiegame,h5games" \
  --cta "Play now,Drop a comment" \
  --footer "Made with RUN.game" \
  --discord-username "yourname"
```

| Option                      | Purpose                                                                        |
| --------------------------- | ------------------------------------------------------------------------------ |
| `--discord-webhook <url>`   | Discord incoming webhook. Stored as a write-only secret and used to auto-post. |
| `--tone <text>`             | Copy tone, e.g. `"hyped but humble"`.                                          |
| `--hashtags a,b,c`          | Hashtags to weave into captions.                                               |
| `--cta "a,b"`               | Preferred calls-to-action.                                                     |
| `--footer <text>`           | Footer appended to copy.                                                       |
| `--discord-username <name>` | Your handle in RUN's community Discord.                                        |

Inspect the current profile any time:

```bash
rundot socials profile show
```

#### Setting up the Discord webhook

In **your** Discord server: **Server Settings → Integrations → Webhooks → New Webhook**, pick the announcement channel, **Copy Webhook URL**, then pass it to `profile set --discord-webhook`. RUN.game stores it as a write-only secret; `profile show` only reports whether it's configured, never the URL itself. Your webhook is stored once on your creator profile and reused across all your games. Without a webhook, Discord is skipped (the other platforms are unaffected).

### 2. Prepare a launch packet

```bash
rundot socials prepare
```

This generates drafts for all platforms against your latest public version. If a Discord webhook is configured, the Discord announcement is **posted immediately**; the rest are left as drafts for you to post.

| Option                                          | Purpose                                                                                  |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `--update latest`                               | Which release to promote. `latest` (default) or a specific `versionNumber`.              |
| `--platforms x,reddit,tiktok,instagram,discord` | Limit to specific platforms (default: all).                                              |
| `--force`                                       | Re-post to Discord even if this version was already auto-posted (idempotent by default). |
| `--json`                                        | Machine-readable output.                                                                 |
| `--game-id <id>`                                | Target a specific game.                                                                  |

Each draft includes three caption variants (`punchy` / `sincere` / `playful`), a tracked link, and any `warnings` (for example, a variant that exceeds X's 280-character limit — the text is never silently truncated).

### 3. Check the posting checklist

```bash
rundot socials status
```

Shows each platform's mode (auto/composer), status (ready / auto-posted / posted), and tracked link.

### 4. Open a composer and post

```bash
# Prints the caption + a prefilled composer URL for X
rundot socials open x

# Pick a caption variant (1–3) and, for Reddit, a target subreddit
rundot socials open reddit --variant 2 --target IndieGaming
```

Copy the printed caption, open the URL, and publish.

#### Per-platform behavior

| Platform               | Behavior                                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------------------------ |
| **Discord**            | Auto-posted via your webhook. No composer to open.                                                     |
| **X**                  | Composer-assisted. `open x` prints a prefilled tweet URL.                                              |
| **Reddit**             | Composer-assisted. `open reddit --target <subreddit>` prints a prefilled submit URL with title + body. |
| **TikTok / Instagram** | No prefilled web composer — copy the caption and post from the app.                                    |

#### Link placement

Captions aren't equally link-friendly across platforms, so each draft tells you where the tracked link goes:

* **inline** — the tracked link goes directly in the post (e.g. X, when it doesn't hurt reach).
* **reply** — post the caption first, then add the link + `@RUN` in a reply, which keeps the main post's reach. `open` prints the reply text for you.
* **search** — the platform isn't clickable (TikTok / Instagram). Put the game name and key art on-screen and use the `Search "{game}" on @RUN` call-to-action so viewers can find it in the RUN app.

### 5. Mark posts as published

Once a post is live, record its URL. RUN.game amplifies marked posts from **@RUN**, and it keeps your checklist accurate.

```bash
rundot socials mark-posted x --url https://x.com/you/status/123
```

| Option            | Purpose                                        |
| ----------------- | ---------------------------------------------- |
| `--url <url>`     | The published post URL (required).             |
| `--packet latest` | Which packet to mark (defaults to the latest). |
| `--game-id <id>`  | Target a specific game.                        |

### 6. Verify which steps are finished

Posting isn't the finish line. A step counts as **finished** once it's both:

1. **Posted** — Discord auto-posts; the others are recorded via `mark-posted` (and, where we can, confirmed live on the platform).
2. **Clicked by someone who isn't you** — at least one click on that step's tracked link from a profile that isn't yours.

```bash
rundot socials verify
```

Each platform reports one of three states:

* **not posted** — nothing's up yet.
* **awaiting click** — it's posted, but no one other than you has clicked the link yet. It's *not* done — RUN.game is waiting for **1 click that isn't you** before the step counts.
* **finished ✓** — posted *and* clicked by someone other than you.

TikTok and Instagram have no tracked link (search-only), so they can be marked posted but aren't finishable here.

```bash
rundot socials verify --json            # machine-readable, includes each row's state
rundot socials verify --packet <id>     # a specific packet
```

### Command reference

All commands live under `rundot socials` and require `RUNDOT_BETA_FEATURES=1`.

| Command                              | Purpose                                                         |
| ------------------------------------ | --------------------------------------------------------------- |
| `prepare`                            | Generate a launch packet (auto-posts Discord if configured).    |
| `status`                             | Show the posting checklist.                                     |
| `open <platform>`                    | Print the caption + composer URL for a platform.                |
| `mark-posted <platform> --url <url>` | Record a published post URL.                                    |
| `verify`                             | Check which steps are finished (posted + ≥1 non-creator click). |
| `profile set`                        | Configure webhook / tone / hashtags / footer / CTAs.            |
| `profile show`                       | Show the current social profile.                                |

### How-tos

#### Promote an update end-to-end

```bash
export RUNDOT_BETA_FEATURES=1
rundot socials profile set --discord-webhook "https://discord.com/api/webhooks/…"  # one-time
rundot socials prepare                       # Discord auto-posts; others become drafts
rundot socials open x                         # copy caption, open composer, publish
rundot socials mark-posted x --url https://x.com/you/status/123
rundot socials status                         # confirm the checklist
rundot socials verify                         # which steps are finished (posted + a non-creator click)?
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://series-1.gitbook.io/rundot-docs/v5.22.0/readme/marketing-your-game.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
