[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F9FGNQ06AS5C607WW7KP9J":3,"skill-same:01M6F9FGNQ06AS5C607WW7KP9J":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":56,"ownerHtmlUrl":57,"ownerBlogUrl":6,"ownerDescription":58,"updatedAt":59,"install":60,"resources":72},"01M6F9FGNQ06AS5C607WW7KP9J","customerio-sdk-patterns",null,"Apply production-ready Customer.io SDK patterns.\nUse when implementing typed clients, retry logic, event batching,\nor singleton management for customerio-node.\nTrigger: \"customer.io best practices\", \"customer.io patterns\",\n\"production customer.io\", \"customer.io architecture\", \"customer.io singleton\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcustomerio-sdk-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"sdk","customerio sdk patterns","patterns","edit","customer-io","retry","event","trigger","io","npx","ready","openclaw","write","read","node","Bash","code","architecture","customer","typescript","2026-11-10 10:12:30",false,"---\nname: customerio-sdk-patterns\ndescription: |\n  Apply production-ready Customer.io SDK patterns.\n  Use when implementing typed clients, retry logic, event batching,\n  or singleton management for customerio-node.\n  Trigger: \"customer.io best practices\", \"customer.io patterns\",\n  \"production customer.io\", \"customer.io architecture\", \"customer.io singleton\".\nallowed-tools: Read, Write, Edit, Bash(npm:*), Bash(npx:*), Glob, Grep\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ncompatible-with: claude-code, codex, openclaw\ntags: [saas, customer-io, sdk, patterns, typescript]\n\n---\n# Customer.io SDK Patterns\n\n## Overview\n\nProduction-ready patterns for `customerio-node`: type-safe wrappers with enum-constrained events, retry with exponential backoff, event batching for high-volume scenarios, and singleton lifecycle management.\n\n## Prerequisites\n\n- `customerio-node` installed\n- TypeScript project (recommended for type-safe patterns)\n- Understanding of your event taxonomy\n\n## Instructions\n\n### Pattern 1: Type-Safe Client Wrapper\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-typed.ts\nimport { TrackClient, RegionUS, RegionEU } from \"customerio-node\";\n\n\u002F\u002F Define your event taxonomy as a union type\ntype CioEvent =\n  | { name: \"signed_up\"; data: { method: string; source?: string } }\n  | { name: \"plan_changed\"; data: { from: string; to: string; mrr: number } }\n  | { name: \"feature_used\"; data: { feature: string; duration_ms?: number } }\n  | { name: \"checkout_completed\"; data: { order_id: string; total: number; items: number } }\n  | { name: \"subscription_cancelled\"; data: { reason: string; feedback?: string } };\n\n\u002F\u002F Define user attributes with strict types\ninterface CioUserAttributes {\n  email: string;\n  first_name?: string;\n  last_name?: string;\n  plan?: \"free\" | \"starter\" | \"pro\" | \"enterprise\";\n  company?: string;\n  created_at?: number;       \u002F\u002F Unix seconds\n  last_seen_at?: number;     \u002F\u002F Unix seconds\n  [key: string]: unknown;    \u002F\u002F Allow additional attributes\n}\n\nexport class TypedCioClient {\n  private client: TrackClient;\n\n  constructor(siteId: string, apiKey: string, region: \"us\" | \"eu\" = \"us\") {\n    this.client = new TrackClient(siteId, apiKey, {\n      region: region === \"eu\" ? RegionEU : RegionUS,\n    });\n  }\n\n  async identify(userId: string, attributes: CioUserAttributes): Promise\u003Cvoid> {\n    await this.client.identify(userId, {\n      ...attributes,\n      last_seen_at: Math.floor(Date.now() \u002F 1000),\n    });\n  }\n\n  async track(userId: string, event: CioEvent): Promise\u003Cvoid> {\n    await this.client.track(userId, {\n      name: event.name,\n      data: { ...event.data, tracked_at: Math.floor(Date.now() \u002F 1000) },\n    });\n  }\n\n  async suppress(userId: string): Promise\u003Cvoid> {\n    await this.client.suppress(userId);\n  }\n\n  async destroy(userId: string): Promise\u003Cvoid> {\n    await this.client.destroy(userId);\n  }\n}\n```\n\n### Pattern 2: Retry with Exponential Backoff\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-retry.ts\ninterface RetryOptions {\n  maxRetries: number;\n  baseDelayMs: number;\n  maxDelayMs: number;\n  jitterFactor: number;  \u002F\u002F 0 to 1\n}\n\nconst DEFAULT_RETRY: RetryOptions = {\n  maxRetries: 3,\n  baseDelayMs: 1000,\n  maxDelayMs: 30000,\n  jitterFactor: 0.3,\n};\n\nasync function withRetry\u003CT>(\n  fn: () => Promise\u003CT>,\n  opts: RetryOptions = DEFAULT_RETRY\n): Promise\u003CT> {\n  let lastError: Error | undefined;\n\n  for (let attempt = 0; attempt \u003C= opts.maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (err: any) {\n      lastError = err;\n      const statusCode = err.statusCode ?? err.status;\n\n      \u002F\u002F Don't retry client errors (except 429 rate limit)\n      if (statusCode >= 400 && statusCode \u003C 500 && statusCode !== 429) {\n        throw err;\n      }\n\n      if (attempt === opts.maxRetries) break;\n\n      \u002F\u002F Exponential backoff with jitter\n      const delay = Math.min(\n        opts.baseDelayMs * Math.pow(2, attempt),\n        opts.maxDelayMs\n      );\n      const jitter = delay * opts.jitterFactor * Math.random();\n      await new Promise((r) => setTimeout(r, delay + jitter));\n    }\n  }\n  throw lastError;\n}\n\n\u002F\u002F Usage with Customer.io client\nimport { TrackClient, RegionUS } from \"customerio-node\";\n\nconst cio = new TrackClient(\n  process.env.CUSTOMERIO_SITE_ID!,\n  process.env.CUSTOMERIO_TRACK_API_KEY!,\n  { region: RegionUS }\n);\n\n\u002F\u002F Wrap any operation with retry\nawait withRetry(() =>\n  cio.identify(\"user-123\", { email: \"user@example.com\" })\n);\n\nawait withRetry(() =>\n  cio.track(\"user-123\", { name: \"page_viewed\", data: { url: \"\u002Fpricing\" } })\n);\n```\n\n### Pattern 3: Event Queue with Batching\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-batch.ts\nimport { TrackClient, RegionUS } from \"customerio-node\";\n\ninterface QueuedEvent {\n  userId: string;\n  name: string;\n  data?: Record\u003Cstring, any>;\n}\n\nexport class CioBatchTracker {\n  private queue: QueuedEvent[] = [];\n  private timer: NodeJS.Timeout | null = null;\n  private client: TrackClient;\n\n  constructor(\n    private readonly batchSize: number = 50,\n    private readonly flushIntervalMs: number = 5000\n  ) {\n    this.client = new TrackClient(\n      process.env.CUSTOMERIO_SITE_ID!,\n      process.env.CUSTOMERIO_TRACK_API_KEY!,\n      { region: RegionUS }\n    );\n    this.startTimer();\n  }\n\n  enqueue(userId: string, name: string, data?: Record\u003Cstring, any>): void {\n    this.queue.push({ userId, name, data });\n    if (this.queue.length >= this.batchSize) {\n      this.flush();\n    }\n  }\n\n  async flush(): Promise\u003Cvoid> {\n    if (this.queue.length === 0) return;\n    const batch = this.queue.splice(0, this.batchSize);\n    const concurrency = 10;\n\n    for (let i = 0; i \u003C batch.length; i += concurrency) {\n      const chunk = batch.slice(i, i + concurrency);\n      await Promise.allSettled(\n        chunk.map((event) =>\n          this.client.track(event.userId, {\n            name: event.name,\n            data: event.data,\n          })\n        )\n      );\n    }\n  }\n\n  private startTimer(): void {\n    this.timer = setInterval(() => this.flush(), this.flushIntervalMs);\n  }\n\n  async shutdown(): Promise\u003Cvoid> {\n    if (this.timer) clearInterval(this.timer);\n    await this.flush();\n  }\n}\n\n\u002F\u002F Usage\nconst tracker = new CioBatchTracker(50, 5000);\n\n\u002F\u002F Non-blocking — events are queued and flushed automatically\ntracker.enqueue(\"user-1\", \"page_viewed\", { url: \"\u002Fhome\" });\ntracker.enqueue(\"user-2\", \"button_clicked\", { button: \"cta\" });\n\n\u002F\u002F On process exit\nprocess.on(\"SIGTERM\", async () => {\n  await tracker.shutdown();\n  process.exit(0);\n});\n```\n\n### Pattern 4: Singleton with Validation\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-singleton.ts\nimport { TrackClient, APIClient, RegionUS, RegionEU } from \"customerio-node\";\n\nclass CioClientFactory {\n  private static trackInstance: TrackClient | null = null;\n  private static appInstance: APIClient | null = null;\n\n  static getTrackClient(): TrackClient {\n    if (!this.trackInstance) {\n      const siteId = process.env.CUSTOMERIO_SITE_ID;\n      const apiKey = process.env.CUSTOMERIO_TRACK_API_KEY;\n      if (!siteId || !apiKey) {\n        throw new Error(\n          \"Missing CUSTOMERIO_SITE_ID or CUSTOMERIO_TRACK_API_KEY. \" +\n          \"Set these in your environment or .env file.\"\n        );\n      }\n      const region = process.env.CUSTOMERIO_REGION === \"eu\" ? RegionEU : RegionUS;\n      this.trackInstance = new TrackClient(siteId, apiKey, { region });\n    }\n    return this.trackInstance;\n  }\n\n  static getAppClient(): APIClient {\n    if (!this.appInstance) {\n      const appKey = process.env.CUSTOMERIO_APP_API_KEY;\n      if (!appKey) {\n        throw new Error(\n          \"Missing CUSTOMERIO_APP_API_KEY. \" +\n          \"Set this in your environment or .env file.\"\n        );\n      }\n      const region = process.env.CUSTOMERIO_REGION === \"eu\" ? RegionEU : RegionUS;\n      this.appInstance = new APIClient(appKey, { region });\n    }\n    return this.appInstance;\n  }\n\n  \u002F** Reset for testing *\u002F\n  static reset(): void {\n    this.trackInstance = null;\n    this.appInstance = null;\n  }\n}\n\n\u002F\u002F Usage — same instance everywhere\nconst cio = CioClientFactory.getTrackClient();\nconst api = CioClientFactory.getAppClient();\n```\n\n## Pattern Summary\n\n| Pattern | When to Use | Key Benefit |\n|---------|------------|-------------|\n| Typed Client | Always | Compile-time safety on events + attributes |\n| Retry + Backoff | Production API calls | Handles transient 5xx and 429 errors |\n| Batch Queue | High-volume tracking (>100 events\u002Fsec) | Reduces connection overhead, respects rate limits |\n| Singleton Factory | Multi-module apps | Prevents connection leaks, validates config once |\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| Type mismatch | Wrong event data shape | Use TypeScript union types for events |\n| Queue memory growth | Events produced faster than flushed | Lower `batchSize`, increase flush frequency |\n| Retry exhausted (3x) | Persistent API failure | Check credentials, Customer.io status page |\n| Singleton null credentials | Env vars not loaded | Ensure `dotenv` loads before client creation |\n\n## Resources\n\n- [customerio-node SDK](https:\u002F\u002Fgithub.com\u002Fcustomerio\u002Fcustomerio-node)\n- [Track API Rate Limits](https:\u002F\u002Fdocs.customer.io\u002Fintegrations\u002Fapi\u002Ftrack\u002F)\n- [About Customer.io APIs](https:\u002F\u002Fdocs.customer.io\u002Fintegrations\u002Fapi\u002Fcustomerio-apis\u002F)\n\n## Next Steps\n\nAfter implementing patterns, proceed to `customerio-primary-workflow` for messaging workflows.\n\n","# Customer.io SDK Patterns\n\n## Overview\n\nProduction-ready patterns for `customerio-node`: type-safe wrappers with enum-constrained events, retry with exponential backoff, event batching for high-volume scenarios, and singleton lifecycle management.\n\n## Prerequisites\n\n- `customerio-node` installed\n- TypeScript project (recommended for type-safe patterns)\n- Understanding of your event taxonomy\n\n## Instructions\n\n### Pattern 1: Type-Safe Client Wrapper\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-typed.ts\nimport { TrackClient, RegionUS, RegionEU } from \"customerio-node\";\n\n\u002F\u002F Define your event taxonomy as a union type\ntype CioEvent =\n  | { name: \"signed_up\"; data: { method: string; source?: string } }\n  | { name: \"plan_changed\"; data: { from: string; to: string; mrr: number } }\n  | { name: \"feature_used\"; data: { feature: string; duration_ms?: number } }\n  | { name: \"checkout_completed\"; data: { order_id: string; total: number; items: number } }\n  | { name: \"subscription_cancelled\"; data: { reason: string; feedback?: string } };\n\n\u002F\u002F Define user attributes with strict types\ninterface CioUserAttributes {\n  email: string;\n  first_name?: string;\n  last_name?: string;\n  plan?: \"free\" | \"starter\" | \"pro\" | \"enterprise\";\n  company?: string;\n  created_at?: number;       \u002F\u002F Unix seconds\n  last_seen_at?: number;     \u002F\u002F Unix seconds\n  [key: string]: unknown;    \u002F\u002F Allow additional attributes\n}\n\nexport class TypedCioClient {\n  private client: TrackClient;\n\n  constructor(siteId: string, apiKey: string, region: \"us\" | \"eu\" = \"us\") {\n    this.client = new TrackClient(siteId, apiKey, {\n      region: region === \"eu\" ? RegionEU : RegionUS,\n    });\n  }\n\n  async identify(userId: string, attributes: CioUserAttributes): Promise\u003Cvoid> {\n    await this.client.identify(userId, {\n      ...attributes,\n      last_seen_at: Math.floor(Date.now() \u002F 1000),\n    });\n  }\n\n  async track(userId: string, event: CioEvent): Promise\u003Cvoid> {\n    await this.client.track(userId, {\n      name: event.name,\n      data: { ...event.data, tracked_at: Math.floor(Date.now() \u002F 1000) },\n    });\n  }\n\n  async suppress(userId: string): Promise\u003Cvoid> {\n    await this.client.suppress(userId);\n  }\n\n  async destroy(userId: string): Promise\u003Cvoid> {\n    await this.client.destroy(userId);\n  }\n}\n```\n\n### Pattern 2: Retry with Exponential Backoff\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-retry.ts\ninterface RetryOptions {\n  maxRetries: number;\n  baseDelayMs: number;\n  maxDelayMs: number;\n  jitterFactor: number;  \u002F\u002F 0 to 1\n}\n\nconst DEFAULT_RETRY: RetryOptions = {\n  maxRetries: 3,\n  baseDelayMs: 1000,\n  maxDelayMs: 30000,\n  jitterFactor: 0.3,\n};\n\nasync function withRetry\u003CT>(\n  fn: () => Promise\u003CT>,\n  opts: RetryOptions = DEFAULT_RETRY\n): Promise\u003CT> {\n  let lastError: Error | undefined;\n\n  for (let attempt = 0; attempt \u003C= opts.maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (err: any) {\n      lastError = err;\n      const statusCode = err.statusCode ?? err.status;\n\n      \u002F\u002F Don't retry client errors (except 429 rate limit)\n      if (statusCode >= 400 && statusCode \u003C 500 && statusCode !== 429) {\n        throw err;\n      }\n\n      if (attempt === opts.maxRetries) break;\n\n      \u002F\u002F Exponential backoff with jitter\n      const delay = Math.min(\n        opts.baseDelayMs * Math.pow(2, attempt),\n        opts.maxDelayMs\n      );\n      const jitter = delay * opts.jitterFactor * Math.random();\n      await new Promise((r) => setTimeout(r, delay + jitter));\n    }\n  }\n  throw lastError;\n}\n\n\u002F\u002F Usage with Customer.io client\nimport { TrackClient, RegionUS } from \"customerio-node\";\n\nconst cio = new TrackClient(\n  process.env.CUSTOMERIO_SITE_ID!,\n  process.env.CUSTOMERIO_TRACK_API_KEY!,\n  { region: RegionUS }\n);\n\n\u002F\u002F Wrap any operation with retry\nawait withRetry(() =>\n  cio.identify(\"user-123\", { email: \"user@example.com\" })\n);\n\nawait withRetry(() =>\n  cio.track(\"user-123\", { name: \"page_viewed\", data: { url: \"\u002Fpricing\" } })\n);\n```\n\n### Pattern 3: Event Queue with Batching\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-batch.ts\nimport { TrackClient, RegionUS } from \"customerio-node\";\n\ninterface QueuedEvent {\n  userId: string;\n  name: string;\n  data?: Record\u003Cstring, any>;\n}\n\nexport class CioBatchTracker {\n  private queue: QueuedEvent[] = [];\n  private timer: NodeJS.Timeout | null = null;\n  private client: TrackClient;\n\n  constructor(\n    private readonly batchSize: number = 50,\n    private readonly flushIntervalMs: number = 5000\n  ) {\n    this.client = new TrackClient(\n      process.env.CUSTOMERIO_SITE_ID!,\n      process.env.CUSTOMERIO_TRACK_API_KEY!,\n      { region: RegionUS }\n    );\n    this.startTimer();\n  }\n\n  enqueue(userId: string, name: string, data?: Record\u003Cstring, any>): void {\n    this.queue.push({ userId, name, data });\n    if (this.queue.length >= this.batchSize) {\n      this.flush();\n    }\n  }\n\n  async flush(): Promise\u003Cvoid> {\n    if (this.queue.length === 0) return;\n    const batch = this.queue.splice(0, this.batchSize);\n    const concurrency = 10;\n\n    for (let i = 0; i \u003C batch.length; i += concurrency) {\n      const chunk = batch.slice(i, i + concurrency);\n      await Promise.allSettled(\n        chunk.map((event) =>\n          this.client.track(event.userId, {\n            name: event.name,\n            data: event.data,\n          })\n        )\n      );\n    }\n  }\n\n  private startTimer(): void {\n    this.timer = setInterval(() => this.flush(), this.flushIntervalMs);\n  }\n\n  async shutdown(): Promise\u003Cvoid> {\n    if (this.timer) clearInterval(this.timer);\n    await this.flush();\n  }\n}\n\n\u002F\u002F Usage\nconst tracker = new CioBatchTracker(50, 5000);\n\n\u002F\u002F Non-blocking — events are queued and flushed automatically\ntracker.enqueue(\"user-1\", \"page_viewed\", { url: \"\u002Fhome\" });\ntracker.enqueue(\"user-2\", \"button_clicked\", { button: \"cta\" });\n\n\u002F\u002F On process exit\nprocess.on(\"SIGTERM\", async () => {\n  await tracker.shutdown();\n  process.exit(0);\n});\n```\n\n### Pattern 4: Singleton with Validation\n\n```typescript\n\u002F\u002F lib\u002Fcustomerio-singleton.ts\nimport { TrackClient, APIClient, RegionUS, RegionEU } from \"customerio-node\";\n\nclass CioClientFactory {\n  private static trackInstance: TrackClient | null = null;\n  private static appInstance: APIClient | null = null;\n\n  static getTrackClient(): TrackClient {\n    if (!this.trackInstance) {\n      const siteId = process.env.CUSTOMERIO_SITE_ID;\n      const apiKey = process.env.CUSTOMERIO_TRACK_API_KEY;\n      if (!siteId || !apiKey) {\n        throw new Error(\n          \"Missing CUSTOMERIO_SITE_ID or CUSTOMERIO_TRACK_API_KEY. \" +\n          \"Set these in your environment or .env file.\"\n        );\n      }\n      const region = process.env.CUSTOMERIO_REGION === \"eu\" ? RegionEU : RegionUS;\n      this.trackInstance = new TrackClient(siteId, apiKey, { region });\n    }\n    return this.trackInstance;\n  }\n\n  static getAppClient(): APIClient {\n    if (!this.appInstance) {\n      const appKey = process.env.CUSTOMERIO_APP_API_KEY;\n      if (!appKey) {\n        throw new Error(\n          \"Missing CUSTOMERIO_APP_API_KEY. \" +\n          \"Set this in your environment or .env file.\"\n        );\n      }\n      const region = process.env.CUSTOMERIO_REGION === \"eu\" ? RegionEU : RegionUS;\n      this.appInstance = new APIClient(appKey, { region });\n    }\n    return this.appInstance;\n  }\n\n  \u002F** Reset for testing *\u002F\n  static reset(): void {\n    this.trackInstance = null;\n    this.appInstance = null;\n  }\n}\n\n\u002F\u002F Usage — same instance everywhere\nconst cio = CioClientFactory.getTrackClient();\nconst api = CioClientFactory.getAppClient();\n```\n\n## Pattern Summary\n\n| Pattern | When to Use | Key Benefit |\n|---------|------------|-------------|\n| Typed Client | Always | Compile-time safety on events + attributes |\n| Retry + Backoff | Production API calls | Handles transient 5xx and 429 errors |\n| Batch Queue | High-volume tracking (>100 events\u002Fsec) | Reduces connection overhead, respects rate limits |\n| Singleton Factory | Multi-module apps | Prevents connection leaks, validates config once |\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| Type mismatch | Wrong event data shape | Use TypeScript union types for events |\n| Queue memory growth | Events produced faster than flushed | Lower `batchSize`, increase flush frequency |\n| Retry exhausted (3x) | Persistent API failure | Check credentials, Customer.io status page |\n| Singleton null credentials | Env vars not loaded | Ensure `dotenv` loads before client creation |\n\n## Resources\n\n- [customerio-node SDK](https:\u002F\u002Fgithub.com\u002Fcustomerio\u002Fcustomerio-node)\n- [Track API Rate Limits](https:\u002F\u002Fdocs.customer.io\u002Fintegrations\u002Fapi\u002Ftrack\u002F)\n- [About Customer.io APIs](https:\u002F\u002Fdocs.customer.io\u002Fintegrations\u002Fapi\u002Fcustomerio-apis\u002F)\n\n## Next Steps\n\nAfter implementing patterns, proceed to `customerio-primary-workflow` for messaging workflows.\n\n",{"name":5,"tags":44,"author":46,"license":15,"version":47,"description":48,"allowed-tools":49,"compatible-with":50},[45,23,19,21,38],"saas","Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Apply production-ready Customer.io SDK patterns.\nUse when implementing typed clients, retry logic, event batching,\nor singleton management for customerio-node.\nTrigger: \"customer.io best practices\", \"customer.io patterns\",\n\"production customer.io\", \"customer.io architecture\", \"customer.io singleton\".\n","Read, Write, Edit, Bash(npm:*), Bash(npx:*), Glob, Grep","claude-code, codex, openclaw","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcustomerio-sdk-patterns",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":61,"prompt":70,"zipAvailable":71},[62,64,67],{"tool":28,"command":63},"npx skills add ComeOnOliver\u002Fskillshub --skill customerio-sdk-patterns",{"tool":65,"command":66},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill customerio-sdk-patterns",{"tool":68,"command":69},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill customerio-sdk-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fcustomerio-sdk-patterns\u002FSKILL.md",true,[],[74,88,98,106,112,119],{"skillNo":75,"name":76,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":84,"publishedAt":39},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[79],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[85,86,87],"model","model hierarchy","model hierarchy skill",{"skillNo":89,"name":90,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":94,"publishedAt":39},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[93],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},[95,96,97],"MCP","zoom","zoom mcp",{"skillNo":99,"name":100,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":39},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[19,104,105,96],"zoom apps sdk","apps",{"skillNo":107,"name":108,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":39},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[108],{"skillNo":113,"name":114,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":118,"publishedAt":39},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[117],{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},[114],{"skillNo":120,"name":121,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":124,"publishedAt":39},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[121]]