# Create webhook subscription (`create_webhook_subscription`)

Subscribes an HTTPS endpoint the merchant controls to one event topic, so Sign Customiser will POST to it when that event occurs. The topic must be one list_webhook_event_types returns, and the URL must be a public HTTPS address. The response includes the signing secret exactly once: it is never readable again, so it has to be stored at that moment. It requires the webhooks:write scope and an idempotency_key.

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

## Purpose

Point one Sign Customiser event topic at an endpoint the merchant runs.

## Annotations

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

## Prerequisites

- A topic from list_webhook_event_types.
- A public HTTPS URL the merchant controls. A private or non-HTTPS address is refused.
- A merchant has connected this store and approved the webhooks:write scope.

## Side effects

- Creates one subscription, so future events of that topic are delivered to the URL as outbound HTTPS requests.
- Issues a signing secret that is returned once and cannot be read back.
- Claims the idempotency_key for at least 24 hours.

## Arguments

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `topic` | string | yes | The event topic to deliver, from list_webhook_event_types. Expected to grow: tolerate unknown values. One of: `product:created`, `order:created`, `form:submitted`. |
| `url` | string | yes | The destination, which must be a public HTTPS address Sign Customiser can reach. A private, loopback or plain-HTTP address is refused. |
| `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 subscription and its show-once signing secret. The secret is returned by this tool and by an exact idempotent replay, and by nothing else.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `request_id` | string | yes |  |
| `store` | object | yes |  |
| `store.id` | integer | yes |  |
| `store.name` | string | yes |  |
| `subscription` | object | no | The created subscription. Its secret is a sibling member, not part of this object. |
| `subscription.id` | integer | yes |  |
| `subscription.topic` | string | yes |  |
| `subscription.url` | string | yes |  |
| `subscription.created_at` | string or null | no |  |
| `subscription.updated_at` | string or null | no |  |
| `secret` | string | no | The show-once signing credential, used to verify the HMAC on every delivery. Returned by this tool and by an exact idempotent replay only. Store it; do not repeat it into a transcript. |
| `idempotent_replay` | boolean | no | True when this call replayed an earlier write carrying the same idempotency_key rather than creating a second subscription. |

## Error cases

| Code | Recovery |
| --- | --- |
| `idempotency_key_conflict` | That key was used with different arguments. Re-send the original arguments or issue a new key; a new key creates a second subscription with its own secret. |
| `insufficient_scope` | The connection was approved without webhooks:write. |
| `missing_idempotency_key` | Send idempotency_key. It is required for this write. |
| `validation_failed` | An unknown topic, whose allowed_values the problem repeats, or a URL that is not a public HTTPS address. |

## Example: Deliver order events to the merchant endpoint

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_webhook_subscription",
    "arguments": {
      "topic": "order:created",
      "url": "https://example.com/webhooks/sign-customiser",
      "idempotency_key": "18293a4b-5c6d-4e0f-9b5c-6d7e8f901234"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "subscription": {
        "id": 42,
        "topic": "order:created",
        "url": "https://example.com/webhooks/sign-customiser",
        "created_at": "2026-07-22T00:00:00Z",
        "updated_at": "2026-07-22T00:00:00Z"
      },
      "secret": "the-show-once-signing-secret",
      "idempotent_replay": false
    }
  }
}
```

## Example: A URL that is not public HTTPS (error)

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_webhook_subscription",
    "arguments": {
      "topic": "order:created",
      "url": "http://localhost/webhooks",
      "idempotency_key": "18293a4b-5c6d-4e0f-9b5c-6d7e8f901234"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "validation_failed",
        "title": "Validation failed",
        "detail": "The url must be a public HTTPS address Sign Customiser can reach.",
        "recovery": "Correct every field listed in errors, using its pointer to locate the value and allowed_values where one is given, then call the tool again.",
        "pointer": "/url"
      }
    }
  }
}
```
