# Create integration product (`create_integration_product`)

Records a product that already exists in one of the store enabled custom integrations against a customiser, so its price, dimensions, artwork references and captured selections are available to the manufacturer email and export paths. It is an inbound synchronisation: it never downloads the artwork, never calls the integration back and never emits the product-created webhook. There is no delete-product tool on this connector, so the call needs confirm: true and an idempotency_key that makes a retry replay the original product. It requires the products:write scope.

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

## Purpose

Record a product bought through a custom integration against the customiser that designed it, so Sign Customiser can hand it to production.

## Annotations

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

## Prerequisites

- The customiser id, from list_customisers.
- An integration_id for one of the store enabled custom integrations.
- The product identifier the integration uses, its title, its price in minor units and the store currency.
- A merchant has connected this store and approved the products:write scope.

## Side effects

- Creates one product record against the customiser, with its captured selections and artwork references.
- Spends one unit of the store product-write budget.
- Claims the idempotency_key for at least 24 hours.

## Arguments

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `customiser_id` | integer | yes | The customiser the product was designed with, from list_customisers. |
| `integration_id` | integer | yes | One of the store enabled custom integrations. An unknown id is a validation failure naming this field. |
| `store_product_id` | string | yes | The product identifier the integration uses. Unique per integration: a repeat is a validation failure, not a second product. |
| `title` | string | yes | The product title, as the shopper bought it. |
| `description` | string | no | The product description, if the integration has one. |
| `price_amount` | integer | yes | The unit price in integer minor units of currency, never a decimal. |
| `compare_at_amount` | integer | no | The was-price in minor units, which must be at least price_amount. |
| `currency` | string | yes | The ISO 4217 code in upper case. It must equal the store own currency. |
| `width_cm` | number | no | The finished width in centimetres. |
| `height_cm` | number | no | The finished height in centimetres. |
| `length_cm` | number | no | The finished length in centimetres. |
| `customisations` | array of object | no | The selections captured with the design, at most 100 rows. These are what reach the manufacturer email. |
| `customisations[].key` | string | yes | A lower snake_case identifier, unique within this product. The size_cm and size_in keys cannot be combined with width_cm, height_cm or length_cm. |
| `customisations[].label` | string | yes | What the merchant sees for this row. |
| `customisations[].value` | string | yes | What the shopper chose. |
| `artwork` | object | no | References to the production artwork the integration already hosts. Every member is optional and every URL is stored as given. |
| `artwork.preview_image_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.outline_image_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.svg_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.eps_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.illustrator_pdf_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.dxf_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.custom_background_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.custom_background_original_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `artwork.source_file_url` | string | no | A credential-free HTTPS URL the integration already hosts. Sign Customiser stores the reference and never downloads it. |
| `confirm` | boolean | no | Must be true to apply the change, because this connector has no tool that deletes a product again. 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 product record, and whether the call replayed an earlier one. 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 |  |
| `product` | object | no | The created product, with the fields get_integration_product publishes. |
| `idempotent_replay` | boolean | no | True when this call replayed an earlier write carrying the same idempotency_key rather than recording a second product. Read from the response Idempotency-Replay header, not from the v3 body. |
| `links` | object | no | The merchant-admin page for the owning customiser. |
| `links.admin_url` | string | yes |  |

## Error cases

| Code | Recovery |
| --- | --- |
| `amount_overflow` | price_amount or compare_at_amount is outside the range the store currency can carry. |
| `confirmation_required` | Send confirm: true. There is no tool on this connector that deletes a product again. |
| `currency_mismatch` | currency must be the store own currency; the problem names it. |
| `idempotency_key_conflict` | That key was used with different arguments. Re-send the original arguments or issue a new key. |
| `insufficient_scope` | The connection was approved without products:write. |
| `missing_idempotency_key` | Send idempotency_key. It is required for this write. |
| `rate_limited` | The store product-write budget is exhausted; wait retry_after seconds. |
| `resource_not_found` | No customiser with that id exists in this store. |
| `validation_failed` | A duplicate store_product_id for this integration, an unknown integration_id, or an artwork URL that is not credential-free HTTPS within 255 characters. |

## Example: Record a neon sign bought through an integration

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_integration_product",
    "arguments": {
      "customiser_id": 42,
      "integration_id": 3,
      "store_product_id": "ext-90210",
      "title": "Custom neon sign — \"Open\"",
      "price_amount": 24900,
      "currency": "GBP",
      "width_cm": 60,
      "height_cm": 20,
      "customisations": [
        {
          "key": "text",
          "label": "Text",
          "value": "Open"
        }
      ],
      "confirm": true,
      "idempotency_key": "f6071829-3b4c-4e2f-9a3b-4c5d6e7f8091"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "product": {
        "id": 5001,
        "customiser_id": 42,
        "title": "Custom neon sign — \"Open\"",
        "description": null,
        "price_amount": 24900,
        "compare_at_amount": null,
        "currency": "GBP",
        "image_url": null,
        "width_cm": 60,
        "height_cm": 20,
        "length_cm": null,
        "created_at": "2026-08-24T00:00:00Z",
        "updated_at": "2026-08-24T00:00:00Z"
      },
      "idempotent_replay": false
    }
  }
}
```

## Example: Without confirmation nothing is recorded (error)

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_integration_product",
    "arguments": {
      "customiser_id": 42,
      "integration_id": 3,
      "store_product_id": "ext-90210",
      "title": "Custom neon sign",
      "price_amount": 24900,
      "currency": "GBP",
      "idempotency_key": "f6071829-3b4c-4e2f-9a3b-4c5d6e7f8091"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "confirmation_required",
        "title": "Confirmation required",
        "detail": "Recording product ext-90210 against customiser 42 cannot be undone through this connector: there is no delete-product tool.",
        "recovery": "This operation changes something consequential. Send confirm: true once the merchant has agreed to it.",
        "parameter": "confirm",
        "allowed_values": [
          "true"
        ]
      }
    }
  }
}
```
