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.

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.
| Field | Use |
|---|---|
id | Order id, part of the idempotency key |
line_items[].id | Line id, completes the idempotency key |
line_items[].selling_plan_id | Present when the line was a subscription purchase |
line_items[].title | Prompt 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
- Shopify WebMCP checkout: what agents can do and where a Sume clip fits
Shopify's Sep 28 WebMCP support lets browser agents read and complete checkout. A Sume product clip belongs on the product page before it, not inside checkout.
- A Supabase job table for Sume webhooks: upsert on job_id
Sume retries webhooks up to 10 times. A Postgres table keyed on job_id with ON CONFLICT turns repeat deliveries into no-ops. Schema, SQL and the order of steps.
- TikTok Ads MCP: 400-tool or 40-tool URL beside Sume's hosted MCP?
TikTok's Agentic Hub has a Full Disclosure URL (about 400 tools) and a Progressive one (about 40). Which to pick with Sume's hosted MCP server also connected.
- ubuntu-latest moves to 26.04: test your Sume workflow now
GitHub is moving ubuntu-latest to Ubuntu 26.04 between Oct 19 and Nov 19. Run a Sume API smoke test on both images, or pin 24.04, before it flips.
Written by Sume