{"name":"TikTok Shop workflow","nodes":[{"id":"3f5a1c2d-0001-4a1b-8c2d-9e0f11220001","name":"Daily Creator Roster Review","type":"n8n-nodes-base.scheduleTrigger","typeVersion":1.2,"position":[-260,300],"parameters":{"rule":{"interval":[{"field":"days","daysInterval":1}]}}},{"id":"3f5a1c2d-0002-4a1b-8c2d-9e0f11220002","name":"Fetch Shop Orders (7d)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[0,160],"parameters":{"url":"https://open-api.tiktokglobalshop.com/order/202309/orders/search","sendQuery":true,"queryParameters":{"parameters":[{"name":"shop_cipher","value":"SHOP_CIPHER"},{"name":"page_size","value":"100"},{"name":"create_time_ge","value":"={{ Math.floor(Date.now()/1000) - 604800 }}"},{"name":"create_time_lt","value":"={{ Math.floor(Date.now()/1000) }}"}]},"options":{}}},{"id":"3f5a1c2d-0003-4a1b-8c2d-9e0f11220003","name":"Fetch Creator Performance","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[0,440],"parameters":{"url":"https://open-api.tiktokglobalshop.com/affiliate_seller/202405/creators/performance","sendQuery":true,"queryParameters":{"parameters":[{"name":"shop_cipher","value":"SHOP_CIPHER"},{"name":"date_range","value":"LAST_7_DAYS"},{"name":"fields","value":"creator_id,creator_handle,follower_count,video_count,live_count,video_views,sample_cost,fulfillment_cost,partnered_at"},{"name":"page_size","value":"100"}]},"options":{}}},{"id":"3f5a1c2d-0004-4a1b-8c2d-9e0f11220004","name":"Merge Orders + Creator Stats","type":"n8n-nodes-base.merge","typeVersion":3,"position":[260,300],"parameters":{"mode":"append","options":{}}},{"id":"3f5a1c2d-0005-4a1b-8c2d-9e0f11220005","name":"Attribute Orders To Creators","type":"n8n-nodes-base.code","typeVersion":2,"position":[520,300],"parameters":{"jsCode":"// ---- TikTok Shop: attribute orders to affiliate creators -------------------\n// Input is an APPEND merge of two feeds:\n//   /api/orders/search        -> rows with order_id\n//   /api/affiliate/creator/performance -> rows with creator_id\nconst rows = $input.all().map(i => i.json);\n\nconst orderRows = rows.filter(r => r.order_id);\nconst perfRows  = rows.filter(r => !r.order_id && r.creator_id);\n\n// 1) dedupe orders (the search endpoint pages can overlap on cursor retries)\nconst seen = new Set();\nconst orders = [];\nfor (const o of orderRows) {\n  const key = String(o.order_id);\n  if (seen.has(key)) continue;\n  seen.add(key);\n  orders.push(o);\n}\n\n// 2) roll orders up per creator, respecting order state\nconst CANCELLED = ['CANCELLED', 'UNPAID', 'CANCELLING'];\nconst REFUNDED  = ['REFUNDED', 'PARTIALLY_REFUNDED', 'RETURN_COMPLETED'];\nconst agg = {};\nfor (const o of orders) {\n  const cid = o.affiliate_creator_id || o.creator_id;\n  if (!cid) continue;                                  // organic / ads order, not creator driven\n  const status = String(o.order_status || '').toUpperCase();\n  if (CANCELLED.includes(status)) continue;            // never shipped, no money moved\n\n  const gmv   = Number(o.payment?.total_amount ?? o.total_amount ?? 0);\n  const units = Number(o.sku_count ?? o.line_items?.length ?? 1);\n  const rate  = Number(o.affiliate_commission_rate ?? 0.15);   // fraction, e.g. 0.15\n  const comm  = Number(o.affiliate_commission_amount ?? gmv * rate);\n  const refunded = REFUNDED.includes(status);\n\n  const a = agg[cid] || (agg[cid] = {\n    creator_id: cid,\n    creator_handle: o.affiliate_creator_handle || null,\n    orders: 0, units: 0, gross_gmv: 0, refunded_gmv: 0,\n    commission: 0, commission_rate_sum: 0, first_order_at: null, last_order_at: null,\n  });\n\n  a.orders += 1;\n  a.units  += units;\n  a.gross_gmv += gmv;\n  a.commission += comm;\n  a.commission_rate_sum += rate;\n  if (refunded) a.refunded_gmv += gmv;\n\n  const t = o.create_time ? Number(o.create_time) * 1000 : Date.parse(o.created_at || '') || null;\n  if (t) {\n    if (!a.first_order_at || t < a.first_order_at) a.first_order_at = t;\n    if (!a.last_order_at  || t > a.last_order_at)  a.last_order_at  = t;\n  }\n}\n\n// 3) attach the creator performance feed (views, samples, content volume)\nconst perfById = {};\nfor (const p of perfRows) perfById[String(p.creator_id)] = p;\n\nconst now = Date.now();\nconst out = [];\nconst allIds = new Set([...Object.keys(agg), ...Object.keys(perfById)]);\n\nfor (const cid of allIds) {\n  const a = agg[cid] || { creator_id: cid, orders: 0, units: 0, gross_gmv: 0,\n    refunded_gmv: 0, commission: 0, commission_rate_sum: 0, first_order_at: null, last_order_at: null };\n  const p = perfById[cid] || {};\n\n  const daysSinceLastSale = a.last_order_at\n    ? Math.round((now - a.last_order_at) / 86400000)\n    : 999;\n\n  out.push({\n    creator_id: cid,\n    creator_handle: a.creator_handle || p.creator_handle || ('creator_' + cid),\n    follower_count: Number(p.follower_count ?? 0),\n    video_count: Number(p.video_count ?? 0),\n    live_count: Number(p.live_count ?? 0),\n    video_views: Number(p.video_views ?? 0),\n    orders: a.orders,\n    units_sold: a.units,\n    gross_gmv: Math.round(a.gross_gmv * 100) / 100,\n    refunded_gmv: Math.round(a.refunded_gmv * 100) / 100,\n    commission_paid: Math.round(a.commission * 100) / 100,\n    avg_commission_rate: a.orders ? Math.round((a.commission_rate_sum / a.orders) * 1000) / 1000 : 0,\n    sample_cost: Number(p.sample_cost ?? 0),          // free product we shipped them\n    shipping_cost: Number(p.fulfillment_cost ?? 0),\n    days_since_last_sale: daysSinceLastSale,\n    days_in_program: p.partnered_at\n      ? Math.max(1, Math.round((now - Date.parse(p.partnered_at)) / 86400000))\n      : 30,\n  });\n}\n\nreturn out.map(r => ({ json: r }));"}},{"id":"3f5a1c2d-0006-4a1b-8c2d-9e0f11220006","name":"Score Creator ROI","type":"n8n-nodes-base.code","typeVersion":2,"position":[780,300],"parameters":{"jsCode":"// ---- Per-creator ROI + roster decision -------------------------------------\n// Economics: net = (kept GMV x gross margin) - commission - sample - shipping\n// Cost of the creator = everything we actually paid them / shipped them.\nconst GROSS_MARGIN = 0.62;      // product margin before creator costs\nconst TARGET_ROI   = 1.60;      // 1.6x on creator spend = keep\nconst WATCH_ROI    = 1.00;      // break-even .. target = watchlist\nconst GRACE_DAYS   = 21;        // new creators get a ramp window\nconst MIN_ORDERS   = 3;         // below this the ROI number is noise\nconst WINDOW_DAYS  = 7;         // the orders feed above is a 7-day lookback\n\nconst rows = $input.all().map(i => i.json);\nconst scored = rows.map(r => {\n  const keptGmv = Math.max(0, r.gross_gmv - r.refunded_gmv);\n  const refundRate = r.gross_gmv > 0 ? r.refunded_gmv / r.gross_gmv : 0;\n\n  const creatorCost = r.commission_paid + r.sample_cost + r.shipping_cost;\n  const contribution = keptGmv * GROSS_MARGIN;\n  const netProfit = contribution - creatorCost;\n  const roi = creatorCost > 0 ? contribution / creatorCost : (contribution > 0 ? 99 : 0);\n\n  // efficiency signals\n  const gpm = r.video_views > 0 ? (keptGmv / r.video_views) * 1000 : 0;   // GMV per 1k views\n  const cvr = r.video_views > 0 ? r.orders / r.video_views : 0;\n  const aov = r.orders > 0 ? keptGmv / r.orders : 0;\n  const contentCadence = r.days_in_program > 0\n    ? (r.video_count + r.live_count) / (r.days_in_program / 7) : 0;       // posts per week\n\n  // pacing: the orders feed is a fixed 7-day window, so the daily rate is\n  // netProfit / 7 - NOT / days_in_program (that would divide 7 days of sales\n  // by a months-long tenure and under-project every established creator).\n  // Creators younger than the window only have days_in_program days of data.\n  const observedDays = Math.max(1, Math.min(WINDOW_DAYS, r.days_in_program || WINDOW_DAYS));\n  const dailyProfit = netProfit / observedDays;\n  const projected30dProfit = Math.round(dailyProfit * 30 * 100) / 100;\n\n  // 0-100 blended score: ROI is king, then consistency, then reach quality\n  const roiScore   = Math.min(roi / (TARGET_ROI * 1.5), 1) * 55;\n  const volScore   = Math.min(r.orders / 25, 1) * 20;\n  const gpmScore   = Math.min(gpm / 40, 1) * 15;\n  const cadScore   = Math.min(contentCadence / 3, 1) * 10;\n  const refundPen  = refundRate > 0.15 ? (refundRate - 0.15) * 100 : 0;   // returns hurt\n  const stalePen   = r.days_since_last_sale > 30 ? 10 : 0;\n  const score = Math.max(0, Math.round(roiScore + volScore + gpmScore + cadScore - refundPen - stalePen));\n\n  // Only genuinely new partners get the benefit of the doubt. A creator who has\n  // been on the roster for months and still has < MIN_ORDERS is not \"ramping\",\n  // they are dead weight - otherwise nothing would ever get dropped.\n  const newCreator = r.days_in_program <= GRACE_DAYS;\n  const tooThinToJudge = newCreator && r.orders < MIN_ORDERS;\n\n  let tier, action, reason;\n  if (roi >= TARGET_ROI && r.orders >= MIN_ORDERS) {\n    tier = score >= 75 ? 'A_hero' : 'B_keep';\n    action = 'reengage';\n    reason = `ROI ${roi.toFixed(2)}x on $${creatorCost.toFixed(2)} paid, ${r.orders} orders`;\n  } else if (roi >= WATCH_ROI || tooThinToJudge) {\n    tier = tooThinToJudge ? 'C_ramping' : 'C_watch';\n    action = 'watch';\n    reason = tooThinToJudge\n      ? `only ${r.days_in_program}d in program / ${r.orders} orders - still inside the ${GRACE_DAYS}d grace window`\n      : `ROI ${roi.toFixed(2)}x is above break-even but below target ${TARGET_ROI}x, keeping on watchlist`;\n  } else {\n    tier = 'D_drop';\n    action = 'drop';\n    reason = `ROI ${roi.toFixed(2)}x, net $${netProfit.toFixed(2)} after $${creatorCost.toFixed(2)} of commission+samples`;\n  }\n\n  // re-engagement offer scales with proven ROI (never above 25% commission)\n  const offerRate = action === 'reengage'\n    ? Math.min(0.25, Math.round((r.avg_commission_rate + (roi >= 3 ? 0.05 : 0.02)) * 100) / 100)\n    : r.avg_commission_rate;\n\n  return {\n    ...r,\n    kept_gmv: Math.round(keptGmv * 100) / 100,\n    refund_rate: Math.round(refundRate * 1000) / 1000,\n    creator_cost: Math.round(creatorCost * 100) / 100,\n    net_profit: Math.round(netProfit * 100) / 100,\n    roi: Math.round(roi * 100) / 100,\n    gpm: Math.round(gpm * 100) / 100,\n    cvr: Math.round(cvr * 10000) / 10000,\n    aov: Math.round(aov * 100) / 100,\n    content_cadence_per_week: Math.round(contentCadence * 100) / 100,\n    projected_30d_profit: projected30dProfit,\n    score,\n    tier,\n    action,\n    reason,\n    is_new_creator: newCreator,\n    proposed_commission_rate: offerRate,\n    evaluated_at: new Date().toISOString(),\n  };\n});\n\nscored.sort((a, b) => b.score - a.score);\nreturn scored.map(r => ({ json: r }));"}},{"id":"3f5a1c2d-0007-4a1b-8c2d-9e0f11220007","name":"Drop Zero-Activity Creators","type":"n8n-nodes-base.filter","typeVersion":2,"position":[1040,300],"parameters":{"conditions":{"options":{"caseSensitive":true,"version":2},"combinator":"or","conditions":[{"id":"f1","leftValue":"={{ $json.orders }}","rightValue":0,"operator":{"type":"number","operation":"gt"}},{"id":"f2","leftValue":"={{ $json.video_views }}","rightValue":500,"operator":{"type":"number","operation":"gt"}}]},"options":{}}},{"id":"3f5a1c2d-0008-4a1b-8c2d-9e0f11220008","name":"ROI Above Target?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1300,300],"parameters":{"conditions":{"options":{"caseSensitive":true,"version":2},"combinator":"and","conditions":[{"id":"c1","leftValue":"={{ $json.action }}","rightValue":"reengage","operator":{"type":"string","operation":"equals"}}]},"options":{}}},{"id":"3f5a1c2d-0009-4a1b-8c2d-9e0f11220009","name":"Build Re-Engage Offer","type":"n8n-nodes-base.set","typeVersion":3.3,"position":[1560,100],"parameters":{"assignments":{"assignments":[{"id":"a1","name":"creator_id","value":"={{ $json.creator_id }}","type":"string"},{"id":"a2","name":"creator_handle","value":"={{ $json.creator_handle }}","type":"string"},{"id":"a3","name":"new_commission_rate","value":"={{ $json.proposed_commission_rate }}","type":"number"},{"id":"a4","name":"roi","value":"={{ $json.roi }}","type":"number"},{"id":"a5","name":"message","value":"=Hey @{{ $json.creator_handle }} — your last 7 days did {{ $json.orders }} orders at {{ $json.roi }}x ROI for us. Bumping you to {{ ($json.proposed_commission_rate * 100).toFixed(0) }}% commission + a free restock if you keep posting. Deal?","type":"string"}]},"options":{}}},{"id":"3f5a1c2d-0010-4a1b-8c2d-9e0f11220010","name":"Send Creator Re-Engage Offer","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1820,100],"parameters":{"method":"POST","url":"https://open-api.tiktokglobalshop.com/affiliate_seller/202405/open_collaborations/invite","sendBody":true,"specifyBody":"json","jsonBody":"={{ JSON.stringify({ shop_cipher: \"SHOP_CIPHER\", creator_id: $json.creator_id, commission_rate: $json.new_commission_rate, free_sample: true, invitation_message: $json.message }) }}","options":{}}},{"id":"3f5a1c2d-0011-4a1b-8c2d-9e0f11220011","name":"Still In Grace Window?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1560,500],"parameters":{"conditions":{"options":{"caseSensitive":true,"version":2},"combinator":"and","conditions":[{"id":"c2","leftValue":"={{ $json.action }}","rightValue":"watch","operator":{"type":"string","operation":"equals"}}]},"options":{}}},{"id":"3f5a1c2d-0012-4a1b-8c2d-9e0f11220012","name":"Flag Creator To Watchlist","type":"n8n-nodes-base.set","typeVersion":3.3,"position":[1820,380],"parameters":{"assignments":{"assignments":[{"id":"w1","name":"roster_change","value":"watchlist","type":"string"},{"id":"w2","name":"recheck_after_days","value":"={{ $json.is_new_creator ? 14 : 7 }}","type":"number"},{"id":"w3","name":"note","value":"={{ $json.reason }}","type":"string"}]},"options":{}}},{"id":"3f5a1c2d-0013-4a1b-8c2d-9e0f11220013","name":"Mark Creator For Drop","type":"n8n-nodes-base.set","typeVersion":3.3,"position":[1820,620],"parameters":{"assignments":{"assignments":[{"id":"d1","name":"roster_change","value":"dropped","type":"string"},{"id":"d2","name":"note","value":"={{ $json.reason }}","type":"string"},{"id":"d3","name":"commission_reclaimed","value":"={{ $json.creator_cost }}","type":"number"}]},"options":{}}},{"id":"3f5a1c2d-0014-4a1b-8c2d-9e0f11220014","name":"End Collaboration On TikTok","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2080,620],"parameters":{"method":"POST","url":"https://open-api.tiktokglobalshop.com/affiliate_seller/202405/open_collaborations/terminate","sendBody":true,"specifyBody":"json","jsonBody":"={{ JSON.stringify({ shop_cipher: \"SHOP_CIPHER\", creator_id: $json.creator_id, reason: $json.reason }) }}","options":{}}},{"id":"3f5a1c2d-0015-4a1b-8c2d-9e0f11220015","name":"Collect Roster Changes","type":"n8n-nodes-base.merge","typeVersion":3,"position":[2340,300],"parameters":{"mode":"append","options":{}}},{"id":"3f5a1c2d-0016-4a1b-8c2d-9e0f11220016","name":"Build Roster Change Log","type":"n8n-nodes-base.code","typeVersion":2,"position":[2600,300],"parameters":{"jsCode":"// ---- Build the roster change log + a human summary -------------------------\nconst rows = $input.all().map(i => i.json);\n\nconst order = { reengage: 0, watch: 1, drop: 2 };\nrows.sort((a, b) => (order[a.action] ?? 9) - (order[b.action] ?? 9) || b.score - a.score);\n\nconst by = { reengage: [], watch: [], drop: [] };\nfor (const r of rows) (by[r.action] || (by[r.action] = [])).push(r);\n\nconst sum = (arr, k) => arr.reduce((t, r) => t + Number(r[k] || 0), 0);\nconst money = n => '$' + (Math.round(n * 100) / 100).toLocaleString('en-US');\n\nconst droppedCost = sum(by.drop, 'creator_cost');\nconst droppedLoss = sum(by.drop, 'net_profit');\nconst keepProfit  = sum(by.reengage, 'net_profit');\nconst keepProjected = sum(by.reengage, 'projected_30d_profit');\n\nconst line = r => `${r.action === 'drop' ? '❌' : r.action === 'watch' ? '👀' : '✅'} @${r.creator_handle} — ROI ${r.roi}x · ${r.orders} orders · net ${money(r.net_profit)} · score ${r.score} — ${r.reason}`;\n\nconst summary = [\n  `*TikTok Shop creator roster — ${new Date().toISOString().slice(0, 10)}*`,\n  `${rows.length} creators evaluated · keep ${by.reengage.length} · watch ${by.watch.length} · drop ${by.drop.length}`,\n  `Kept roster net profit ${money(keepProfit)} (30d projection ${money(keepProjected)})`,\n  `Dropping frees ${money(droppedCost)} of commission+samples that returned ${money(droppedLoss)}`,\n  '',\n  ...by.reengage.slice(0, 8).map(line),\n  ...by.watch.slice(0, 5).map(line),\n  ...by.drop.slice(0, 8).map(line),\n].join('\\n');\n\nreturn rows.map((r, i) => ({\n  json: {\n    logged_at: new Date().toISOString(),\n    creator_id: r.creator_id,\n    creator_handle: r.creator_handle,\n    tier: r.tier,\n    action: r.action,\n    roi: r.roi,\n    score: r.score,\n    orders: r.orders,\n    kept_gmv: r.kept_gmv,\n    commission_paid: r.commission_paid,\n    creator_cost: r.creator_cost,\n    net_profit: r.net_profit,\n    projected_30d_profit: r.projected_30d_profit,\n    refund_rate: r.refund_rate,\n    gpm: r.gpm,\n    proposed_commission_rate: r.proposed_commission_rate,\n    reason: r.reason,\n    summary: i === 0 ? summary : '',\n  },\n}));"}},{"id":"3f5a1c2d-0017-4a1b-8c2d-9e0f11220017","name":"Append To Creator Roster Sheet","type":"n8n-nodes-base.googleSheets","typeVersion":4.5,"position":[2860,300],"parameters":{"operation":"append","documentId":{"__rl":true,"value":"SHEET_ID","mode":"id"},"sheetName":{"__rl":true,"value":"gid=0","mode":"list","cachedResultName":"Creator Roster"},"columns":{"mappingMode":"autoMapInputData","value":{},"matchingColumns":[]},"options":{}}},{"id":"3f5a1c2d-0018-4a1b-8c2d-9e0f11220018","name":"Post Roster Summary To Slack","type":"n8n-nodes-base.slack","typeVersion":2.2,"position":[3120,300],"parameters":{"select":"channel","channelId":{"__rl":true,"value":"#tiktok-shop","mode":"name"},"text":"={{ $json.summary }}","otherOptions":{}}},{"id":"3f5a1c2d-0019-4a1b-8c2d-9e0f11220019","name":"Note Ingest","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[-300,-60],"parameters":{"content":"## 1. INGEST\nPull the last 7 days of TikTok Shop orders and the affiliate creator performance feed, then append both streams into one dataset. Orders carry the commission actually paid; the performance feed carries views, content volume and what the free samples cost us.","height":320,"width":520,"color":4}},{"id":"3f5a1c2d-0020-4a1b-8c2d-9e0f11220020","name":"Note Economics","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[520,-60],"parameters":{"content":"## 2. ROI MATHS\nDedupe orders, drop cancellations, subtract refunds, then per creator:\nnet = kept GMV x 62% margin - commission - sample - shipping.\nROI >= 1.6x with 3+ orders = keep. Break-even or inside the 21d grace window = watch. Everything else = drop.","height":320,"width":520,"color":5}},{"id":"3f5a1c2d-0021-4a1b-8c2d-9e0f11220021","name":"Note Roster","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1540,-60],"parameters":{"content":"## 3. ROSTER ACTIONS\nWinners get a commission bump invite (capped at 25%). Ramping creators go on a 7/14 day watchlist. Losers get their collaboration terminated, and every change is logged to Sheets + summarised in Slack.","height":320,"width":520,"color":3}}],"connections":{"Daily Creator Roster Review":{"main":[[{"node":"Fetch Shop Orders (7d)","type":"main","index":0},{"node":"Fetch Creator Performance","type":"main","index":0}]]},"Fetch Shop Orders (7d)":{"main":[[{"node":"Merge Orders + Creator Stats","type":"main","index":0}]]},"Fetch Creator Performance":{"main":[[{"node":"Merge Orders + Creator Stats","type":"main","index":1}]]},"Merge Orders + Creator Stats":{"main":[[{"node":"Attribute Orders To Creators","type":"main","index":0}]]},"Attribute Orders To Creators":{"main":[[{"node":"Score Creator ROI","type":"main","index":0}]]},"Score Creator ROI":{"main":[[{"node":"Drop Zero-Activity Creators","type":"main","index":0}]]},"Drop Zero-Activity Creators":{"main":[[{"node":"ROI Above Target?","type":"main","index":0}]]},"ROI Above Target?":{"main":[[{"node":"Build Re-Engage Offer","type":"main","index":0}],[{"node":"Still In Grace Window?","type":"main","index":0}]]},"Build Re-Engage Offer":{"main":[[{"node":"Send Creator Re-Engage Offer","type":"main","index":0}]]},"Send Creator Re-Engage Offer":{"main":[[{"node":"Collect Roster Changes","type":"main","index":0}]]},"Still In Grace Window?":{"main":[[{"node":"Flag Creator To Watchlist","type":"main","index":0}],[{"node":"Mark Creator For Drop","type":"main","index":0}]]},"Flag Creator To Watchlist":{"main":[[{"node":"Collect Roster Changes","type":"main","index":1}]]},"Mark Creator For Drop":{"main":[[{"node":"End Collaboration On TikTok","type":"main","index":0}]]},"End Collaboration On TikTok":{"main":[[{"node":"Collect Roster Changes","type":"main","index":1}]]},"Collect Roster Changes":{"main":[[{"node":"Build Roster Change Log","type":"main","index":0}]]},"Build Roster Change Log":{"main":[[{"node":"Append To Creator Roster Sheet","type":"main","index":0}]]},"Append To Creator Roster Sheet":{"main":[[{"node":"Post Roster Summary To Slack","type":"main","index":0}]]}},"settings":{"executionOrder":"v1"},"pinData":{}}