Shopify selling_plan_id in order webhooks: a Sume welcome clip

Shopify 10.01 order webhooks carry selling_plan_id on line items. Use it to start one Sume welcome video per subscription order, with a safe retry key.

5 min readSume
All posts

How do you send a welcome video only to customers who bought a subscription? Shopify's 10.01 changelog says order webhook events now carry a selling_plan_id field on line item objects (read 2026-10-04). A line item with a selling plan is a subscription line, so your webhook handler can branch on that one field and start a Sume video job only for those orders.

The handler has to do two things well: answer Shopify quickly, and never create a second paid job when Shopify retries the delivery.

Branch on the line item

In an order payload, line_items is an array. Each entry that was bought through a subscription has a non-null selling_plan_id under the 10.01 shape. Loop the lines, keep the ones where that field is present, and create one clip per such line, or one per order if you prefer.

Do not infer a subscription from anything else, such as the product title. The field is the signal Shopify now provides, and an order with a mix of one-time and subscription lines will have both kinds in the same array.

Order webhook fields used by the handler (read 2026-10-04)
FieldUse
idOrder id, part of the idempotency key
line_items[].idLine id, completes the idempotency key
line_items[].selling_plan_idPresent when the line was a subscription purchase
line_items[].titlePrompt text for the clip

The handler and the retry key

Shopify redelivers a webhook when it does not get a timely success, so your handler will sometimes see the same order twice. Sume's Idempotency-Key makes that harmless: the retry returns the original job. Build the key from the order id and the line id so each subscription line maps to exactly one job. The function below runs against a sample payload, so you can test it before wiring it to a live topic.

It uses the image endpoint with model: "sume/auto" for a welcome still. Swap in a video request if that is your product; the key and the early return work the same way.

const sample = {
  id: 1001,
  line_items: [
    { id: 11, title: 'Coffee, monthly', selling_plan_id: 555 },
    { id: 12, title: 'Mug', selling_plan_id: null },
  ],
};

async function handleOrder(order) {
  const key = process.env.SUME_API_KEY;
  if (!key) throw new Error('SUME_API_KEY is not set');
  const subs = order.line_items.filter((l) => l.selling_plan_id != null);
  const out = [];
  for (const line of subs) {
    const res = await fetch('https://api.sume.com/v1/images', {
      method: 'POST',
      headers: {
        'x-api-key': key,
        'content-type': 'application/json',
        'idempotency-key': 'order-' + order.id + '-line-' + line.id,
      },
      body: JSON.stringify({ model: 'sume/auto', prompt: 'Welcome card for ' + line.title }),
    });
    out.push(res.status);
  }
  return out;
}

handleOrder(sample).then((s) => console.log(s)).catch((e) => console.error(e.message));

Answer fast, then wait for the job

Return a success to Shopify as soon as you have queued the work, not after the render. A Sume 202 means accepted, not finished. Store the job id against the order, then either poll GET /v1/jobs/:id/status, honoring next_poll_after_seconds, or use webhook mode and handle job.completed. Sume retries a webhook delivery up to 10 times, 30 seconds apart, with a 10 second timeout, so your receiver should answer quickly too and treat job_id as its own idempotency key.

The post on the five-second webhook limit and a hero image job shows how to split receive and work, and product video automation from a webhook is the broader walkthrough.

Caveats

Test against a real 10.01 payload before relying on the field, since older webhook API versions will not include it. Keep the Sume key on your server, rotate it if it leaks, and send only one auth header. Reusing an idempotency key with a different body returns 409 idempotency_conflict, so if you change the prompt template later, include a template version in the key.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume