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

# Create Webhook Subscription

> Subscribe your app to a webhook topic on the installed store

# Create Webhook Subscription

Subscribes your app to one [topic](https://docs.launchmystore.io/api-reference/webhooks/topics.md) on the
store the access token belongs to. Every event on that topic is then POSTed
to your callback URL, signed with this subscription's own secret.

## Required scope

Subscribing needs the **read scope of the resource the topic is about** —
the same rule Shopify uses. A write scope implies its read scope.

| Topics | Scope (any one of) |
| - | - |
| `orders/*`, `refunds/create`, `carts/*`, `checkouts/create`, `subscriptions/*` | `read_orders` |
| `fulfillments/*` | `read_fulfillments` or `read_orders` |
| `products/*` | `read_products` |
| `collections/*` | `read_products` or `read_collections` |
| `customers/*`, `newsletter/create`, `contact_form/create` | `read_customers` |
| `inventory_levels/update` | `read_inventory` |
| `discounts/*` | `read_discounts` |
| `blogs/*` | `read_content` |
| `themes/publish` | `read_themes` |
| `shop/update`, `app/uninstalled`, `app_subscriptions/update` | none |

Without the topic's scope the request is rejected with `403`. Apps built
when this endpoint required `write_shop` for every topic are still allowed
to subscribe with `write_shop`, but events are only **delivered** while the
installation holds the topic's scope — the response then carries a
`warnings` entry saying so. The scope is checked again on every delivery,
so revoking it stops the data.

## Request

```bash cURL theme={null}
curl -X POST "https://api.launchmystore.io/api/v1/webhooks.json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "orders/create",
    "callbackUrl": "https://your-app.com/webhooks/orders"
  }'
```

```javascript Node.js theme={null}
const response = await fetch('https://api.launchmystore.io/api/v1/webhooks.json', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    topic: 'orders/create',
    callbackUrl: 'https://your-app.com/webhooks/orders',
  }),
});
const { data } = await response.json();
// Store data.webhook.secret now — it is never shown again.
```

The body may also be wrapped in Shopify's resource envelope
(`{ "webhook": { "topic": ..., "address": ... } }`).

## Body Parameters

**topic** `string` *required*

A topic from the [topic reference](https://docs.launchmystore.io/api-reference/webhooks/topics.md) that is
subscribable through the API. `app/installed` and the GDPR topics are not:
declare them in your app manifest instead.

**callbackUrl** `string` *required*

Where deliveries are POSTed. `address` (Shopify's name) is accepted as an
alias. Must be a public `http`/`https` URL in production; use HTTPS.

**format** `string` (default: json)

Only `json` is supported — webhook bodies are always JSON.

## Response

**data.webhook** `object`

**properties**

**id** `string`

Subscription id — use it to delete the subscription.

**topic** `string`

The subscribed topic.

**address** `string`

The callback URL (same as `callbackUrl`).

**format** `string`

Always `json`.

**source** `string`

`api` for subscriptions created here.

**secret** `string`

The HMAC signing secret of this subscription. Returned **only** in this response.

**isEnabled** `boolean`

Always `true` on create.

**data.warnings** `string[]`

Present when the subscription was accepted through the legacy
`write_shop` rule but the installation lacks the topic's scope.

## Example Response

```json theme={null}
{
  "status": 201,
  "state": "success",
  "data": {
    "webhook": {
      "id": "125fc7e5-8600-4075-a962-368615e78a3b",
      "address": "https://your-app.com/webhooks/orders",
      "appWebhookId": "125fc7e5-8600-4075-a962-368615e78a3b",
      "appId": "3b0f5a2e-…",
      "storeId": "ef10744c-…",
      "topic": "orders/create",
      "callbackUrl": "https://your-app.com/webhooks/orders",
      "isEnabled": true,
      "format": "json",
      "source": "api",
      "secret": "9f2c…64 hex characters…",
      "createdAt": "2026-09-27T10:48:12.000Z",
      "updatedAt": "2026-09-27T10:48:12.000Z"
    }
  }
}
```

## Errors

Errors use the standard `{ errors }` body.

| Status | When |
| - | - |
| `400` | The same topic and URL are already subscribed, or the URL is not an acceptable public URL. |
| `403` | The installation lacks the topic's scope. The message names the scope. |
| `422` | Unknown or non-subscribable topic, or a `format` other than `json`. |

```json theme={null}
{
  "errors": {
    "base": ["Subscribing to discounts/create requires the read_discounts scope"]
  }
}
```

## Notes

* Deliveries to this subscription are signed with its own `secret`
  (see [Webhook Verification](https://docs.launchmystore.io/api-reference/webhooks/verification.md)).
  To rotate it, delete the subscription and create it again.
* Subscriptions your app creates here are never changed by your manifest:
  installing, re-authorising or publishing a new app version only manages
  the subscriptions declared in the manifest's `webhooks[]`.
* All of an app's subscriptions on a store are removed when the merchant
  uninstalls the app (after the `app/uninstalled` delivery).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.