# Create order (`create_order`)

Records an order that already exists in one of the store enabled custom integrations, with its external order id and number, grand total, currency, optional customer and address snapshots, and its line items. Each line item references a product already recorded by create_integration_product by its Sign Customiser product id. Creating the order durably schedules the store established follow-on effects: the manufacturer order emails, order analytics, the order.created webhook and onboarding synchronisation. An email to a real manufacturer cannot be recalled, so the call needs confirm: true and an idempotency_key that makes an exact repeat replay the original response without scheduling those effects again. It records an order; it takes no payment and collects no card data. It requires the orders:write scope.

- Source URL: https://www.signcustomiser.com/help/mcp/tools/create_order/
- Markdown URL: https://www.signcustomiser.com/help/mcp/tools/create_order.md
- MCP endpoint: https://web.signcustomiser.com/mcp
- Required scope: `orders:write`
- Behaviour: destructive write

## Purpose

Record an order bought through a custom integration, so Sign Customiser hands its designs to production.

## Annotations

| Hint | Value |
| --- | --- |
| readOnlyHint | false |
| destructiveHint | true |
| idempotentHint | true |
| openWorldHint | true |

## Prerequisites

- Every product on the order already recorded by create_integration_product, so its Sign Customiser product_id exists.
- An integration_id for one of the store enabled custom integrations.
- The store contact email must be set in the merchant admin, or the manufacturer hand-off has nowhere to send.
- A merchant has connected this store and approved the orders:write scope.

## Side effects

- Creates one order and its line items against the store.
- Schedules the manufacturer order emails, which reach a real manufacturer and cannot be recalled.
- Records the order in the store analytics.
- Delivers the order.created webhook to every subscription for that topic.
- Runs onboarding synchronisation for the store.
- Spends one unit of the store order-write budget.
- Claims the idempotency_key for at least 24 hours.

## Arguments

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `integration_id` | integer | yes | One of the store enabled custom integrations. store_order_id is unique within it. |
| `store_order_id` | string | yes | The order identifier the integration uses. A repeat for the same integration is a validation failure, not a second order. |
| `store_order_number` | string | yes | The order number the shopper and the merchant see. |
| `total_amount` | integer | yes | The grand total in integer minor units. Sign Customiser derives its own total from the stored unit prices, so this never overrides the fee basis. |
| `currency` | string | yes | The ISO 4217 code in upper case. It must equal the store currency and the currency of every referenced product. |
| `shipping_method` | string | no | The shipping method the shopper chose, as free text. |
| `customer` | object | no | The customer snapshot the integration supplied. Personal data: repeat only what the merchant asked for. |
| `customer.first_name` | string | no |  |
| `customer.last_name` | string | no |  |
| `customer.email` | string | no |  |
| `customer.phone` | string | no |  |
| `billing_address` | object | no | The billing address snapshot. Every member is optional. Personal data. |
| `billing_address.first_name` | string | no |  |
| `billing_address.last_name` | string | no |  |
| `billing_address.company` | string | no |  |
| `billing_address.address_1` | string | no |  |
| `billing_address.address_2` | string | no |  |
| `billing_address.city` | string | no |  |
| `billing_address.province` | string | no |  |
| `billing_address.postcode` | string | no |  |
| `billing_address.country` | string | no |  |
| `billing_address.email` | string | no |  |
| `billing_address.phone` | string | no |  |
| `shipping_address` | object | no | The delivery address snapshot, which is what reaches the manufacturer hand-off. Every member is optional. Personal data. |
| `shipping_address.first_name` | string | no |  |
| `shipping_address.last_name` | string | no |  |
| `shipping_address.company` | string | no |  |
| `shipping_address.address_1` | string | no |  |
| `shipping_address.address_2` | string | no |  |
| `shipping_address.city` | string | no |  |
| `shipping_address.province` | string | no |  |
| `shipping_address.postcode` | string | no |  |
| `shipping_address.country` | string | no |  |
| `shipping_address.email` | string | no |  |
| `shipping_address.phone` | string | no |  |
| `line_items` | array of object | yes | Between 1 and 100 line items. A line item carries no title, price or design: those come from the product it references. |
| `line_items[].product_id` | integer | yes | The Sign Customiser product id create_integration_product returned. Not the integration own identifier, and distinct across the order. |
| `line_items[].quantity` | integer | yes | How many of that product were bought. |
| `confirm` | boolean | no | Must be true to apply the change, because recording the order schedules the manufacturer emails and the order.created webhook, and an email that has been sent cannot be recalled. Without it the call returns a confirmation_required error and changes nothing. A dry run does not need it. |
| `idempotency_key` | string | no | A client-generated key unique to this logical write, such as a UUID. Required: repeating the call with the same key and the same arguments replays the original result instead of writing twice, and the same key with different arguments is an idempotency_key_conflict. A dry run claims no key. |

## Result

The created order with its line items, and whether the call replayed an earlier one. The response does not enumerate the hand-off effects it scheduled. There is no dry run: the route has no validate twin.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `request_id` | string | yes |  |
| `store` | object | yes |  |
| `store.id` | integer | yes |  |
| `store.name` | string | yes |  |
| `order` | object | no | The created order with its line items, as get_order publishes it. |
| `idempotent_replay` | boolean | no | True when this call replayed an earlier write carrying the same idempotency_key. A replay schedules no further hand-off effects. |

## Error cases

| Code | Recovery |
| --- | --- |
| `confirmation_required` | Send confirm: true. The manufacturer emails this schedules cannot be recalled. |
| `currency_mismatch` | currency must equal the store currency and the currency of every referenced product. |
| `idempotency_key_conflict` | That key was used with different arguments. Re-send the original arguments or issue a new key. |
| `idempotency_key_in_flight` | The first call with this key is still running. Wait retry_after seconds and send the identical call again. |
| `insufficient_scope` | The connection was approved without orders:write. |
| `missing_idempotency_key` | Send idempotency_key. It is required for this write. |
| `missing_store_contact_email` | The store contact email is not set. It has to be completed in the merchant admin before an order can be recorded. |
| `rate_limited` | The store order-write budget is exhausted; wait retry_after seconds. |
| `validation_failed` | A store_order_id already recorded for this integration, an unknown product_id, a repeated product_id, or a quantity outside 1 to 10000. |

## Example: Record a one-line order

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_order",
    "arguments": {
      "integration_id": 3,
      "store_order_id": "ext-ord-4471",
      "store_order_number": "#4471",
      "total_amount": 24900,
      "currency": "GBP",
      "shipping_method": "Tracked 48",
      "customer": {
        "first_name": "Ada",
        "last_name": "Lovelace",
        "email": "ada@example.com"
      },
      "line_items": [
        {
          "product_id": 5001,
          "quantity": 1
        }
      ],
      "confirm": true,
      "idempotency_key": "0718293a-4b5c-4f30-8a4b-5c6d7e8f9012"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "order": {
        "id": 8801,
        "store_order_id": "ext-ord-4471",
        "store_order_number": "#4471",
        "status": "received",
        "status_display_name": "Received",
        "total_amount": 24900,
        "currency": "GBP",
        "shipping_method": "Tracked 48",
        "line_items": [
          {
            "id": 9901,
            "store_product_id": "ext-90210",
            "customiser_id": 42,
            "title": "Custom neon sign — \"Open\"",
            "quantity": 1,
            "unit_price_amount": 24900,
            "currency": "GBP"
          }
        ],
        "created_at": "2026-08-24T00:00:00Z",
        "updated_at": "2026-08-24T00:00:00Z"
      },
      "idempotent_replay": false
    }
  }
}
```

## Example: Without confirmation no order is recorded and no email is sent (error)

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_order",
    "arguments": {
      "integration_id": 3,
      "store_order_id": "ext-ord-4471",
      "store_order_number": "#4471",
      "total_amount": 24900,
      "currency": "GBP",
      "line_items": [
        {
          "product_id": 5001,
          "quantity": 1
        }
      ],
      "idempotency_key": "0718293a-4b5c-4f30-8a4b-5c6d7e8f9012"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "confirmation_required",
        "title": "Confirmation required",
        "detail": "Recording order ext-ord-4471 schedules the manufacturer order emails and the order.created webhook, and an email that has been sent cannot be recalled.",
        "recovery": "This operation changes something consequential. Send confirm: true once the merchant has agreed to it.",
        "parameter": "confirm",
        "allowed_values": [
          "true"
        ]
      }
    }
  }
}
```
