> ## Documentation Index
> Fetch the complete documentation index at: https://aiodapp.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Draft a discount

> shopify_discount_draft builds an AIOD discount and returns a prefilled editor link. It creates nothing in Shopify.

Builds a discount and returns a link to review it in the AIOD editor.

Works for anything AIOD can do: a BOGO, a free gift with purchase, tiered or volume quantity breaks, a fixed bundle price, free shipping over a spend threshold, a percentage or amount off a product or collection, a scheduled seasonal sale.

The same call proposes *changes* to an existing discount when you pass `targetDiscountId` — raising a threshold, extending an end date, swapping the gift, re-shaping a tier ladder.

<Warning>
  **Nothing is created in Shopify.** No discount exists and no shopper can receive one until the merchant opens the returned link and saves it there. Say so when you report back — a staged draft is not a live discount.
</Warning>

<Info>
  **Tool name:** `shopify_discount_draft` · **Previously:** `save_discount_draft` · **Writes nothing to Shopify**
</Info>

## When to use it

**This is the default.** Because it touches nothing, it covers building an offer all the way up to the moment the merchant looks at it.

[Create in test mode](/docs/mcp-server/tools/shopify-discount-publish) is the tool that makes a real discount, and it only applies once the merchant has actually asked for that.

## Parameters

| Name               | Type   | Required | What it does                                                                                                        |
| ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `config`           | object | **Yes**  | The discount, sparse — only the fields you're setting.                                                              |
| `targetDiscountId` | string | No       | An existing discount to propose an edit against. Leave it out to create something new.                              |
| `userMessage`      | string | No       | The merchant's request in their own words. Stored with the draft so the editor and history show what was asked for. |

### About `config`

Shapes come from [Read the discount schema](/docs/mcp-server/tools/shopify-discount-config-schema) and are checked strictly.

**Creating** needs a `template` id. **Editing** needs only the fields that change — they're merged onto the stored configuration, not swapped in for it, which is why you should [read the discount](/docs/mcp-server/tools/shopify-discount-get) first.

## What comes back

### When it works

```json theme={null}
{
  "ok": true,
  "url": "https://admin.shopify.com/store/your-store/apps/aiod-automatic-discounts/app/ai-draft/clx9conv0001",
  "conversationId": "clx9conv0001",
  "config": { "...": "the merged configuration" },
  "warnings": [],
  "message": "Draft staged. The merchant must open the link to review the prefilled discount and save it."
}
```

Give the merchant `url` exactly as it comes — it's a direct link into the AIOD editor in their Shopify admin. The `config` comes back enriched with product titles and thumbnails, so it reads the way the editor will render it.

### When it's rejected

```json theme={null}
{
  "ok": false,
  "issues": [
    { "level": "error", "code": "config_shape_parse_failed", "message": "..." }
  ],
  "simulation": [ ... ],
  "schemaReference": "...",
  "message": "The merged discount did not pass validation. Correct only the affected editable paths, then retry with a sparse patch."
}
```

**The rejection contains its own fix.** Depending on what failed it carries either `schemaReference` (the template's contract) or `fieldSyntaxReference` (exact condition and reward syntax) — one or the other, never both, to stay within the response budget. Use it instead of fetching the schema again.

The [rejection codes are listed on the tools overview](/docs/mcp-server/tools#rejections-are-useful-not-fatal).

| Status | When                                                     |
| ------ | -------------------------------------------------------- |
| `400`  | Missing or invalid `config`, or an unsupported template. |
| `404`  | `targetDiscountId` isn't one of this store's discounts.  |
| `422`  | The discount being edited couldn't be loaded safely.     |

## What it checks before staging

Every draft goes through the same gate, in order:

1. **Shape repair.** Common near-misses — object-wrapped conditions, string operators — are fixed automatically before anything is validated.
2. **Field policy.** For an edit, the *saved* discount's template decides which fields you may change, not the one you sent. Anything outside that allowlist is dropped.
3. **No-op check.** A patch that merges back to an identical rule is rejected — it means the change you meant to make wasn't actually in there.
4. **Empty tier check.** A tier left with nothing meaningful in it is rejected rather than staged silently.
5. **Validation and cart simulation.** Errors or a failed simulation reject the draft. Warnings alone don't.

## Things people ask that land here

* "Set up 20% off the Summer collection for July."
* "Draft a buy 2 get 1 free on t-shirts so I can look at it."
* "Raise the free-shipping threshold on my existing offer from $50 to $75."
* "Extend the Black Friday discount through Cyber Monday."

## Getting it right

* **Always say the draft isn't live** and that the merchant has to open the link and save.
* **Fix only what the rejection names.** Don't rewrite the whole configuration.
* **Never retry a `template_field_out_of_scope` rejection with the same fields.** That setting genuinely isn't editable for that template — say so instead.
* **Put the merchant's own words in `userMessage`**, not a paraphrase of your plan. It's stored and shown back to them.
