@chatarmin/os

中文文档
1.44.0发布于 2026/11/08MITTypeScriptES Module近 30 天下载 2.6万

ChatarminOS 的 SDK 类型安全 - 客户和订阅管理

中文使用文档
文档目录27 节
  1. 特征
  2. 目录
  3. 安装
  4. 快速启动
  5. API 参考
  6. 公司
  7. 联系人
  8. 特征
  9. 计费
  10. 订阅级别( T)
  11. 链接
  12. 上岗
  13. 产品 Webhooks
  14. 常见模式
  15. 特征
  16. 条纹订阅流程
  17. 回填历史数据
  18. 类型脚本支持
  19. 导入类型
  20. 路由器类型
  21. 配置
  22. 获取您的 API 密钥
  23. 处理错误
  24. 常见错误代码
  25. 相关文档
  26. 支助
  27. 许可证

@chatarmin/os (中文(简体) )

Type-security SDK for ChatarminOS——为B2B SaaS管理客户,订阅,特性访问,和计费的全能平台.

npm version TypeScript

特征

  • 🏢 公司管理 - 创建、链接和管理客户公司
  • 🎫 订阅级别 - 界定带有特征和条纹定价的等级
  • ✨ 特性访问 - 检查应享权利并强制执行使用限制
  • 📊 用法跟踪 ——用"一能"支持计算账单
  • 💳 条纹融合 ——报销结账会话,链接订阅
  • 🔗 智能链接 ——智能公司按域名/名/电子邮件进行匹配
  • 👥 联系人同步 ——各公司频繁增加联系人
  • 🚀 统一登入 ——在一个API呼叫中完成设置

目录


安装

pnpm add @chatarmin/os
# or
npm install @chatarmin/os
# or
yarn add @chatarmin/os

快速启动

import { ChatarminOS } from "@chatarmin/os"

const os = new ChatarminOS({
  apiKey: process.env.OS_API_KEY!,
  // Optional: custom base URL for self-hosted
  // baseUrl: 'https://your-os.example.com/api/v1'
})

// Onboard a new customer (single call does everything!)
const result = await os.onboard({
  externalOrgId: "org_abc123", // Your product's org ID
  label: "My Product", // Display label for the product link (required)
  hints: {
    companyName: "Acme Inc",
    domain: "acme.com",
  },
  tierCode: "free",
  contactEmail: "[email protected]",
  contactName: "John Doe",
})

console.log(result.companyId) // Use this for all future API calls
console.log(result.linkStatus) // 'created' | 'already_linked'

API 参考

公司

管理客户公司及其与您产品的联系.

companies.get(input)

找一家由你的外部组织 ID或ChatarminOS公司ID.

const company = await os.companies.get({
  externalOrgId: "org_abc123",
})

if (company) {
  console.log(`Found: ${company.name} (${company.id})`)
} else {
  console.log("Company not linked yet")
}

companies.create(input)

创建一新公司,并与您的产品链接.

const company = await os.companies.create({
  name: "Acme Inc",
  domain: "acme.com",
  externalOrgId: "org_abc123",
  label: "My Product", // Display label for the product link
  contactEmail: "[email protected]",
  contactName: "John Doe",
  createdAt: "2024-01-15T10:30:00.000Z", // Optional: historical date
})

console.log(company.id) // UUID
console.log(company.shortId) // e.g., "acme-inc"

companies.update(input)

更新公司元数据(如回填历史日期).

await os.companies.update({
  companyId: "uuid-here",
  createdAt: "2023-06-01T00:00:00.000Z",
})

联系人

管理与公司有关的人员.

contacts.create(input)

创建或更新单个联系人.

const contact = await os.contacts.create({
  companyId: "comp_xxx",
  email: "[email protected]",
  name: "John Doe",
  role: "CEO",
  isPrimary: true,
  avatarUrl: "https://example.com/avatar.jpg",
  metadata: { slackId: "U123" },
})

if (contact.isNew) {
  console.log("Created new contact")
} else {
  console.log("Updated existing contact")
}

contacts.bulkUpsert(input)

批量创建或更新多个联系人. 建议入职.

const result = await os.contacts.bulkUpsert({
  companyId: "comp_xxx",
  contacts: [
    { email: "[email protected]", name: "Alice", role: "CEO", isPrimary: true },
    { email: "[email protected]", name: "Bob", role: "CTO" },
    { email: "[email protected]", name: "Charlie", role: "Engineer" },
  ],
})

console.log(`Created: ${result.created}, Updated: ${result.updated}`)
// result.contacts contains individual results with IDs

contacts.list(companyId, primaryOnly?)

列出公司的联系人 .

// Get all contacts
const contacts = await os.contacts.list("comp_xxx")

// Get only the primary contact
const [primary] = await os.contacts.list("comp_xxx", true)

特征

检查和管理公司的特性访问.

标识符 : 通过 featureCode 输入(例如. "ai_credit") (中文(简体) ). 答复包括: featureCode 财务报告和审定财务报表 featureAccessId。代码设置为特性创建,不通过 API 重新命名 featureName,或创建新特性并迁移到一个真正的重命名。 见 product-billing-api.md 详细情况.

features.check(input)

请检查access-date=中的日期值 (帮助) 公司能否访问特定功能.

const access = await os.features.check({
  companyId: "comp_xxx",
  featureCode: "ai_credit",
})

console.log({
  enabled: access.isEnabled, // Feature is turned on
  canUse: access.canUse, // Can use right now (not at limit)
  hasQuantity: access.hasQuantity, // Is a quantity-based feature
  usage: access.currentUsage, // Current usage count
  limit: access.quantityLimit, // Max allowed (null = unlimited)
  included: access.includedQuantity, // Free tier before billing
  remaining: access.remaining, // How many left (null = unlimited)
})

if (!access.canUse) {
  throw new Error("Upgrade required")
}

features.listAccess(companyId)

列出公司的所有功能及其接入状态.

const features = await os.features.listAccess("comp_xxx")

for (const f of features) {
  console.log(`${f.featureName}: ${f.isEnabled ? "✓" : "✗"}`)
  if (f.quantityLimit) {
    console.log(`  Usage: ${f.currentUsage} / ${f.quantityLimit}`)
  }
}

features.setAccess(input)

更新特性访问方式 部分配置合并。 。 。 .

// Update by company ID
await os.features.setAccess({
  companyId: "comp_xxx",
  featureCode: "ai_credit",
  isEnabled: true,
  quantityLimit: 1000,
  includedQuantity: 100,
  config: {
    model: "gpt-4",
    maxTokens: 4096,
  },
  source: "manual", // 'subscription' | 'manual' | 'trial' | 'api'
  validUntil: new Date("2025-12-31"), // null for no expiration
})

// Update by external org ID
await os.features.setAccess({
  externalOrgId: "org_abc123",
  featureCode: "team_seats",
  config: { maxSeats: 50 }, // Only updates maxSeats, preserves other config
})

features.getAccessByExternalOrgId(externalOrgId)

使用您的产品外部 Org ID 获取所有特性 .

const result = await os.features.getAccessByExternalOrgId("org_abc123")

console.log(`Company: ${result.companyId}`)
for (const f of result.features) {
  console.log(`${f.featureCode}: ${f.isEnabled}`)
  console.log("  Config:", f.configValues)
}

features.checkByExternalOrgId(externalOrgId, featureCode)

使用外部 Org ID 检查特定特性 .

const access = await os.features.checkByExternalOrgId("org_abc123", "ai_credit")

if (access.canUse) {
  console.log(`Remaining: ${access.remaining}`)
}

计费

跟踪使用情况、索偿订阅和管理账单.

API 参考 : docs/engineering/product-billing-api.md - 要求/答复的完整形状 os.billing.* 方法.

billing.trackUsage(input)

用于调制功能的音轨用法 .

const result = await os.billing.trackUsage({
  companyId: "comp_xxx",
  featureCode: "ai_credit",
  quantity: 5,
  idempotencyKey: `request_${requestId}`, // Prevents double-counting
  metadata: { model: "gpt-4", tokens: 1500 },
})

console.log({
  currentUsage: result.currentUsage,
  remaining: result.remaining,
  billable: result.billableQuantity, // Amount beyond included tier
  isAtLimit: result.isAtLimit,
})

billing.claimCheckout(input)

要求订阅已完成的Frede取出会话.

// In your Stripe checkout success handler
const result = await os.billing.claimCheckout({
  companyId: "comp_xxx",
  checkoutSessionId: "cs_test_xxx", // From Stripe callback
})

if (result.alreadyClaimed) {
  console.log("Subscription was already claimed")
} else {
  console.log(`Claimed subscription: ${result.subscriptionId}`)
}

billing.linkSubscription(input)

将现有的Frade订阅链接到公司.

// When you create subscriptions on YOUR Stripe account
const result = await os.billing.linkSubscription({
  companyId: "comp_xxx",
  stripeSubscriptionId: "sub_xxx",
  stripeCustomerId: "cus_xxx", // Optional: fetched from subscription if omitted
  tierCode: "pro", // Apply tier features
  displayName: "Pro Plan",
})

console.log({
  subscriptionId: result.subscriptionId,
  isNew: !result.alreadyLinked,
  featuresApplied: result.featuresApplied,
})

billing.getStatus(companyIdOrInput)

公司快速条纹订阅状态—— activeSubscriptions (单位:千美元)active 编号 : trialing 编号 : past_due),解决了计费配置,并启用特性计数。 范围为产品链接的开单情况 externalOrgId 提供.

见 产品范围计费概况—— getStatus 实例 用于完整行走+样本反应.

const status = await os.billing.getStatus('ee0f4a19-2b69-4969-8807-d0013f1670ee')

const statusByOrg = await os.billing.getStatus({ externalOrgId: 'org_kKZHiUHBSjdu' })

if (status.activeSubscriptions[0]?.status === 'past_due') {
  // gate access for this external org
}

console.log({
  stripeCustomer: status.billingProfile?.stripeCustomerId,
  subscriptionStatus: status.activeSubscriptions[0]?.status,
  features: status.enabledFeaturesCount,
})

billing.getOverview(input)

合并计费页有效载荷:公司、计费概况、主要订阅和最近的发票.

const overview = await os.billing.getOverview({
  companyId: "comp_xxx",
  invoiceLimit: 12,
  includeUsage: true, // optional: embed usage rows
})

console.log({
  plan: overview.subscription?.tier?.name,
  status: overview.subscription?.status,
  paymentMethod: overview.billingProfile?.primaryPaymentMethod,
  invoices: overview.invoices.length,
  usageRows: overview.usage?.length,
})

companies.list 编号 : companies.search

const newest = await os.companies.list({ sort: 'createdAt', limit: 50 })
const matches = await os.companies.search({ query: 'acme', sort: 'createdAt' })

sort 默认为 id。 。 。 . createdAt 是最新的第一,光标包括时间戳,所以链接不会跳过行.

await os.companies.update({
  externalOrgId: 'org_abc123',
  name: 'Acme GmbH',
  domain: 'acme.com',
})

const links = await os.companies.listLinks({ externalOrgId: 'org_abc123' })
await os.contacts.update({
  companyId: company.id,
  email: '[email protected]',
  role: 'Owner',
})

await os.contacts.unlink({ companyId: company.id, email: '[email protected]' })

unlink 取消公司的联系人。 只有在没有其他公司继续使用时,个人记录才会被删除.

billing.listClientUsage(input)

在一个时期内对每个关联客户端的使用. 更喜欢循环 getUsage 给管理员看看. startingOn 包容性, endingBefore 这是独家的,最长366天。 API 键将调用范围限制到其产品; 设置 includeAllProducts: true 将每个产品都纳入该组织。 可选 companyIds 财务报告和审定财务报表 externalOrgIds 缩小结果。 奥米 featureCode 以返回所有使用过的特性.

const usage = await os.billing.listClientUsage({
  startingOn: new Date('2026-06-01T00:00:00.000Z'),
  endingBefore: new Date('2026-07-01T00:00:00.000Z'),
  includeAllProducts: true,
})

const filtered = await os.billing.listClientUsage({
  startingOn: new Date('2026-06-01T00:00:00.000Z'),
  endingBefore: new Date('2026-07-01T00:00:00.000Z'),
  companyIds: ['f9c9b060-6f83-4b57-a34a-0d84951767cb'],
  externalOrgIds: ['org_FVCNOtEdpeeP'],
  featureCode: 'ai_credit',
})

usage.companies[].links[].features[] 包含 usage, (中文(简体) ), includedQuantity, (中文(简体) ), billableQuantity,以及 estimatedOverageCents津贴是应享权利的数量,不按比例分配给窗口.

billing.listClientRevenue(input)

工作人员读取:每个客户的发票、付款和退款都用Strepe发票OS镜像所建。 范围为API关键产品组织. 与该产品有联系的客户为: cx_linked; 所有其他为 no_cx_link 并分别分列.

每个客户每个货币有一个条目。 税前收入净额分为订阅、计算(按 meterCode编号 :featureCode,比如说. chatarmin_ai_02),设置费和一次性金额. periodBasis 实值 invoice_period 违约(Stripe的发票期,即拖欠账单的使用情况)或 paid_at获取页面 limit 财务报告和审定财务报表 cursor,以及 totals 覆盖每一页. completeness.issues 解释一下可能缺少什么.

const revenue = await os.billing.listClientRevenue({
  startingOn: new Date('2026-03-01T00:00:00.000Z'),
  endingBefore: new Date('2026-04-01T00:00:00.000Z'),
  externalOrgIds: ['org_FVCNOtEdpeeP'],
})

const next = revenue.nextCursor
  ? await os.billing.listClientRevenue({
      startingOn: new Date('2026-03-01T00:00:00.000Z'),
      endingBefore: new Date('2026-04-01T00:00:00.000Z'),
      cursor: revenue.nextCursor,
    })
  : null

见 docs/engineering/product-billing-api.md 用于行分类、完整性代码和退款回填.

billing.getSalesCommission(input)

工作人员为:每个账户执行人一个月的佣金,按阅读时计算。 在赢得Pipedrive客户交易时拥有该客户交易的AE在月份支付的发票的许可证行数;在月份贷记或退还的许可证被扣除。 使用费(AI, contacts,Meta),设置费和其他一次性收费从不计数.

const october = await os.billing.getSalesCommission({
  month: '2026-10',
  accountExecutives: ['1001', '1002'], // Pipedrive user ids
  dealsWonFrom: '2026-05-01',
  sdrs: ['2001'],
})
// october.accountExecutives[i]: { name, paidMrrCents, deductionMrrCents, netMrrCents, payments, deductions }

见 docs/engineering/product-billing-api.md 归属和行规则.

billing.getUsage(companyIdOrInput, options?)

Metronome风格的用法聚合. 回返 { data: UsageRecord[] } ——地貌第一地筑巢,每个地貌计费周期.

公司范围: 通过 companyId 作为第一个字符串参数,或通过输入对象 companyId 或者说 externalOrgId (一个要求)。 偏爱 externalOrgId 当CX只知道Org swit——使用范围与产品链接.

// CX-native (single product link)
const { data } = await os.billing.getUsage({
  externalOrgId: 'org_FVCNOtEdpeeP',
  windowSize: 'none',
})

// Or companyId as first arg
const byCompany = await os.billing.getUsage('comp_xxx', { windowSize: 'none' })
  • data[].featureCode - 稳定的地物弹
  • data[].cycles[] 这一功能的计费周期(periodOffset,范围, usage[](中文(简体) )

windowSize 控制是否为 usage 分数是时间总和或时间桶. 过滤器为 featureCode (平稳弹),不是 featureAccessId 看到了吗 特性标识符。 。 。 .

periodOffsetRange (整个计费周期的时间范围):

意图periodOffsetRange
当前周期( 默认)删除或 { from: 0, to: 0 }
仅上一个{ from: -1, to: -1 }
当前 + 前一个{ from: -1, to: 0 }
12个循环拾取器{ from: -11, to: 0 }

多周期范围要求 windowSize: 'none' (最多12个周期)。 无法结合 startingOn编号 :endingBefore。 。 。 .

每个 cycles[] 桶包括 subscriptionItem 财务报告和审定财务报表 summary 当它有用行. usage[] 保持稀疏(value + 日期).

响应形状 :

interface UsageQueryResult {
  data: UsageRecord[]
}

interface UsageRecord {
  featureCode: string
  featureName: string
  cycles: UsageCycleBucket[]
}

interface UsageCycleBucket {
  periodOffset: number
  startingOn: Date
  endingBefore: Date
  label: string
  subscriptionItem?: UsageSubscriptionItem // present when cycle has usage rows
  summary?: UsageCycleSummary // totals + projection; present when cycle has usage rows
  usage: UsagePoint[] // thin: value + dates (+ optional identity/grouping)
}

interface UsagePoint {
  value: number
  startingOn: Date
  endingBefore: Date
  featureAccessId?: string
  meterCode?: string
  groupKey?: string | null // with groupBy: 'productLink'
  groupValue?: string | null
  productLinkId?: string | null // groupBy: 'productLink', or bucketed multi-link companyId
  productLinkLabel?: string | null
  externalOrgId?: string | null
}

读取帐单 cycle.summary 财务报告和审定财务报表 cycle.subscriptionItem - 不是从 usage[] 点.

productLinkId (+标签, externalOrgId)出现于 usage[] 点 groupBy: 'productLink',或装入桶 companyId 多个产品链接共享公司时的询问( 无) groupKey) (中文(简体) ).

windowSizecycles[].usage计费背景
'none'1点subscriptionItem + 键 summary (总是)
'day' 编号 : 'week' 编号 : 'month'N 细点相同的——每轮开一次帐,而不是每桶开一次帐

实例 windowSize

windowSize: 'none' - 总期间:

{
  "data": [{
    "featureCode": "messages",
    "featureName": "Messages",
    "cycles": [{
      "periodOffset": 0,
      "startingOn": "2026-07-01T00:00:00.000Z",
      "endingBefore": "2026-08-01T00:00:00.000Z",
      "label": "Jul 1, 2026 – Aug 1, 2026",
      "subscriptionItem": {
        "includedQuantity": 100,
        "unitAmountCents": 500,
        "currency": "eur"
      },
      "summary": {
        "totalUsage": 120,
        "billableQuantity": 20,
        "estimatedOverageCents": 10000,
        "projectedBillableQuantity": 240,
        "projectedOverageCents": 70000
      },
      "usage": [{
        "value": 120,
        "startingOn": "2026-07-01T00:00:00.000Z",
        "endingBefore": "2026-08-01T00:00:00.000Z"
      }]
    }]
  }]
}

windowSize: 'day' 每日图表桶:

{
  "data": [{
    "featureCode": "messages",
    "featureName": "Messages",
    "cycles": [{
      "periodOffset": 0,
      "subscriptionItem": { "includedQuantity": 100 },
      "summary": { "totalUsage": 20, "billableQuantity": 0 },
      "usage": [
        { "value": 8,  "startingOn": "2026-07-01T00:00:00.000Z", "endingBefore": "2026-07-02T00:00:00.000Z" },
        { "value": 12, "startingOn": "2026-07-02T00:00:00.000Z", "endingBefore": "2026-07-03T00:00:00.000Z" }
      ]
    }]
  }]
}

多周期 (单位:千美元)periodOffsetRange: { from: -1, to: 0 }:

{
  "data": [{
    "featureCode": "messages",
    "featureName": "Messages",
    "cycles": [
      { "periodOffset": 0, "label": "Jul 1 – Aug 1", "summary": { "totalUsage": 120, "billableQuantity": 20 }, "usage": [{ "value": 120 }] },
      { "periodOffset": -1, "label": "Jun 1 – Jul 1", "summary": { "totalUsage": 98, "billableQuantity": 0 }, "usage": [{ "value": 98 }] }
    ]
  }]
}

全部实例: docs/engineering/product-billing-api.md#billinggetusage

const { data } = await os.billing.getUsage('comp_xxx', { windowSize: 'none' })
const messages = data.find((r) => r.featureCode === 'messages')
const cycle = messages?.cycles[0]
const currentTotal = cycle?.usage[0]?.value
const overage = cycle?.summary?.billableQuantity
const cost = cycle?.summary?.estimatedOverageCents

const daily = await os.billing.getUsage('comp_xxx', {
  windowSize: 'day',
  featureCode: 'messages',
})
const dailySeries = (daily.data[0]?.cycles[0]?.usage ?? []).map((point) => ({
  day: point.startingOn.toISOString().slice(0, 10),
  usage: point.value,
}))

// Previous billing period totals
const lastMonth = await os.billing.getUsage('comp_xxx', {
  windowSize: 'none',
  periodOffsetRange: { from: -1, to: -1 },
})

// Last 12 billing cycles (cycle picker)
const history = await os.billing.getUsage('comp_xxx', {
  windowSize: 'none',
  periodOffsetRange: { from: -11, to: 0 },
})
// history.data[0].cycles → one bucket per period; cycle.usage[0] → period total

// Custom sub-range within the current billing period (both dates required)
// usage[] points cover the sub-range; summary totals match it; projections use the full cycle
const week = await os.billing.getUsage('comp_xxx', {
  windowSize: 'day',
  featureCode: 'messages',
  startingOn: new Date('2026-07-10T00:00:00Z'),
  endingBefore: new Date('2026-07-17T00:00:00Z'),
})
windowSize形状
'none'每个特性一分(多个产品链接共享一个公司时的折合总额;使用) groupBy: 'productLink' 每行)
'day' 编号 : 'week' 编号 : 'month'每桶每地一分;多个产品链接分享公司时每桶每链接一分(productLinkId 每个点)
时间范围何时使用
删除当前计费期(违约)
periodOffsetRange: { from: -1, to: -1 }上一个计费期
periodOffsetRange: { from: -11, to: 0 }过去12个计费周期
startingOn + 键 endingBefore内部的子距离 当前 周期(计算;总数按子范围排列;预测使用全周期)

billing.exportUsage(companyIdOrInput, options?)

将总使用量导出为 CSV. 接受与 getUsage (加上隐含) format: 'csv'CSV包括a periodOffset 栏(第一栏),用于多周期出口。 文件名使用导出周期界限 : usage_{windowSize}_{companyId}_{periodStart}_{periodEnd}.csv。 。 。 .

const csv = await os.billing.exportUsage({
  externalOrgId: 'org_FVCNOtEdpeeP',
  windowSize: 'none',
  periodOffsetRange: { from: -11, to: 0 },
})
// csv.filename → "usage_none_<companyId>_2026-06-01_2026-08-01.csv"
// csv.content → first line:
// periodOffset,featureCode,featureName,value,startingOn,endingBefore,groupKey,groupValue,productLinkId,productLinkLabel,externalOrgId,includedQuantity,billableQuantity,quantityLimit,remaining,percentUsed,unitAmountCents,currency,estimatedOverageCents,projectedBillableQuantity,projectedOverageCents

billing.listInvoices(companyIdOrInput, options?)

列出公司发票(不包括汇票/票).

const { invoices } = await os.billing.listInvoices("comp_xxx", { limit: 24 })

billing.getPaymentMethod(companyIdOrInput)

已解决的收费情况付款方法摘要.

const { billingProfile } = await os.billing.getPaymentMethod("comp_xxx")

billing.createPortalSession(input)

开放Frede客户端 用于管理收费流量. returnUrl 必须在允许的产品主机上(products.metadata.billing.returnUrlHosts) (中文(简体) ).

const { url } = await os.billing.createPortalSession({
  companyId: "comp_xxx",
  returnUrl: "https://app.example.com/settings/billing",
})

// When a company has multiple Stripe customers, target a specific billing profile:
const { url: profileUrl } = await os.billing.createPortalSession({
  companyId: "comp_xxx",
  stripeCustomerId: "cus_xxx",
  returnUrl: "https://app.example.com/settings/billing",
})

// Redirect the user to url

billing.resolveInvoicePaymentUrl(input)

解决特定发票的托管发票付款URL.

const { hostedInvoiceUrl } = await os.billing.resolveInvoicePaymentUrl({
  companyId: "comp_xxx",
  invoiceId: "inv_xxx",
})

billing.listPastDuePaymentUrls(companyIdOrInput)

列出过期的发票以及付款地址.

const { items } = await os.billing.listPastDuePaymentUrls("comp_xxx")

billing.sendPastDueInvoice(input)

通过 Strape 电子邮件过期发票( 当收集方法为 send_invoice) (中文(简体) ).

await os.billing.sendPastDueInvoice({
  companyId: "comp_xxx",
  invoiceId: "inv_xxx",
})

billing.syncCompanyFromStripe(input)

调取Frede的帐单、订阅和功能链接.

const result = await os.billing.syncCompanyFromStripe({
  companyId: "comp_xxx",
  stripeCustomerId: "cus_xxx",
})

console.log({
  subscriptionsSynced: result.subscriptionsSynced,
  featuresLinked: result.featuresLinked,
})

billing.relinkBillingProfile(input)

将公司开具的开票情况换成新的"条纹"客户(破坏性).

await os.billing.relinkBillingProfile({
  companyId: "comp_xxx",
  stripeCustomerId: "cus_new_xxx",
})

billing.ensureFeatureSubscription(input)

确保一个磁盘特性与条纹订阅项目相链接.

const result = await os.billing.ensureFeatureSubscription({
  companyId: "comp_xxx",
  featureCode: "ai_credit",
})

console.log(result.action) // 'already_linked' | 'linked_existing' | 'created_new' | 'failed'

billing.setUsage(input)

设置绝对用法( 更正/ 管理) 。 需要一个 reason 用于审计.

const result = await os.billing.setUsage({
  companyId: "comp_xxx",
  featureCode: "messages",
  absoluteValue: 420,
  reason: "Reconcile after import",
})

console.log({
  previous: result.previousUsage,
  current: result.currentUsage,
  delta: result.delta,
})

billing.getUsageHistory(companyId, featureCode, options?)

用于审计/调试的原始使用事件(不是总卷).

const events = await os.billing.getUsageHistory("comp_xxx", "messages", {
  limit: 50,
  eventType: "increment",
})

订阅级别( T)

管理订阅级别并应用功能.

tiers.list()

列出您产品的所有可用级别.

const tiers = await os.tiers.list()

for (const tier of tiers) {
  console.log(`${tier.name} (${tier.code})`)
}
// ['Free', 'Pro', 'Enterprise']

tiers.get(tierCode)

获得详细的分级信息,包括花旗价格.

const tier = await os.tiers.get("pro")

// Check base fee
if (tier.baseFee) {
  console.log(`Base fee: ${tier.baseFee.stripe.unitAmount / 100}€/mo`)
  console.log(`Price ID: ${tier.baseFee.stripe.priceId}`)
}

// Access features
for (const feature of tier.features) {
  console.log(`${feature.code}:`, feature.configValues)
}

// Get all Stripe price IDs (base fee + feature prices)
console.log("Stripe prices:", tier.stripePriceIds)
// ['price_base', 'price_feature1', 'price_feature2']

tiers.apply(tierCode, options)

对公司应用分级特征(不含花纹).

// Apply free tier
await os.tiers.apply("free", {
  companyId: "comp_xxx",
})

// Apply with custom limits
await os.tiers.apply("trial", {
  companyId: "comp_xxx",
  features: {
    ai_credit: { included_quantity: 100 },
    seats: { max_quantity: 10 },
  },
})

subscriptions.create(tierCode, options)

别名 tiers.apply() 采用订阅级.

await os.subscriptions.create("pro", {
  companyId: "comp_xxx",
  features: {
    ai_credit: { included_quantity: 500 },
  },
})

人工管理产品链接.

links.create(input)

在您的外部 Org ID 和现有的公司之间建立人工链接 .

await os.links.create({
  companyId: "comp_xxx",
  externalOrgId: "org_abc123",
  label: "Store Vienna", // Optional: display label
  externalUserId: "user_xyz", // Optional: user who created the link
})

links.reset(options?)

删除您产品的所有产品链接 。 重新导入前使用 .

// Basic reset (links only)
const result = await os.links.reset()
console.log(`Deleted ${result.linksDeleted} links`)

// Full reset (links + contacts + MRR)
const result = await os.links.reset({
  deleteContacts: true,
  resetMrr: true,
})
console.log(
  `Deleted ${result.linksDeleted} links, ${result.contactsDeleted} contacts`
)

上岗

快速客户登机注册.

这个 onboard() 方法控件 :

  1. 智能公司配对/创建(最佳 externalOrgId(中文(简体) )
  2. 产品链接
  3. 自由级特性访问当 tierCode: 'free' (无条纹)
  4. 提供离职或订阅身份证时可选的条纹索赔/链接

付费计费设置另行设置: companies.setupOnboardBilling() (一心重试.

基本用途

const result = await os.onboard({
  externalOrgId: "org_abc123", // Your product's org ID (required)
  label: "My Product", // Display label for the product link (required)
  hints: {
    companyName: "Acme Inc",
    domain: "acme.com",
    emails: ["[email protected]"],
  },
  tierCode: "free",
  contactEmail: "[email protected]",
  contactName: "John Doe",
  createdAt: "2024-01-15T10:30:00.000Z", // Optional: historical date
})

console.log({
  companyId: result.companyId,
  companyName: result.companyName,
  linkStatus: result.linkStatus, // 'created' | 'already_linked'
  tierApplied: result.billingStatus?.tierApplied,
})

设想1:用户完成的条纹检查

const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  hints: { companyName: "Acme Inc", domain: "acme.com" },
  checkoutSessionId: "cs_test_xxx", // From Stripe checkout callback
})

// result.billingStatus.claimed = true
// result.billingStatus.subscriptionId = 'sub_xxx'

假想2: 自由等级注册

const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  hints: { companyName: "Acme Inc" },
  tierCode: "free",
  contactEmail: "[email protected]",
})

// result.billingStatus.tierApplied = true

情景3:您创建条形订阅

// 1. Get tier prices
const tier = await os.tiers.get("pro")

// 2. Create subscription on YOUR Stripe account
const stripeSub = await stripe.subscriptions.create({
  customer: stripeCustomerId,
  items: tier.stripePriceIds.map((price) => ({ price })),
})

// 3. Onboard with the subscription
const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  hints: { companyName: "Acme Inc" },
  stripeSubscriptionId: stripeSub.id,
  stripeCustomerId: stripeCustomerId,
  tierCode: "pro",
})

// result.billingStatus.linked = true
// result.billingStatus.tierApplied = true

工作人员创建的工作空间

新公司被贴上标签 acquisition: 'plg' (自签. 当工作人员创建 客户端的工作空间(例如从您的管理员仪表板),通过 origin: "staff": 公司获得 没有获取标记,也没有自签名处理(签名默认,重复建议).

const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  hints: { companyName: "Acme Inc" },
  origin: "staff",
})

处理现有客户

const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  hints: { companyName: "Acme Inc" },
})

switch (result.linkStatus) {
  case "already_linked":
    console.log("Welcome back!")
    break
  case "created":
    console.log("New company created!")
    break
}

产品 Webhooks

当ChatarminOS推动公司和产品链接改变您的应用程序时,在处理有效载荷之前,先用SDK验证请求.

import { webhooks } from "@chatarmin/os/webhooks"

export async function POST(request: Request) {
  const rawBody = await request.text()

  const event = webhooks.constructEvent(
    rawBody,
    request.headers,
    process.env.OS_WEBHOOK_SIGNING_SECRET!,
  )

  switch (`${event.type}.${event.action}`) {
    case "company.updated":
      // event.data, event.webhookId, event.idempotency_key
      break
  }

  return Response.json({ received: true })
}

使用 ChatarminOS.webhooks.constructEvent 如果您已经导入了主客户端。 脚踏实地 event.webhookId 或者说 event.idempotency_key。 。 。 .


常见模式

特征

async function checkFeatureAccess(orgId: string, feature: string) {
  const access = await os.features.checkByExternalOrgId(orgId, feature)

  if (!access.isEnabled) {
    throw new Error(`Feature ${feature} is not available on your plan`)
  }

  if (!access.canUse) {
    throw new Error(`Usage limit reached for ${feature}. Please upgrade.`)
  }

  return access
}

// Usage
await checkFeatureAccess("org_abc123", "ai_credit")
await performAIOperation()
await os.billing.trackUsage({
  companyId: company.id,
  featureCode: "ai_credit",
  quantity: 1,
})

条纹订阅流程

// 1. User selects a plan → Get tier prices
const tier = await os.tiers.get("pro")

// 2. Create Stripe Checkout Session with tier prices
const session = await stripe.checkout.sessions.create({
  customer: customerId,
  line_items: tier.stripePriceIds.map((price) => ({
    price,
    quantity: 1,
  })),
  success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${baseUrl}/cancel`,
})

// 3. After checkout completes → Claim subscription
const result = await os.onboard({
  externalOrgId: "org_abc123",
  label: "My Product",
  checkoutSessionId: session.id,
})

回填历史数据

import { ChatarminOS } from "@chatarmin/os"
import historicalData from "./migration-data.json"

const os = new ChatarminOS({ apiKey: process.env.OS_API_KEY! })

for (const org of historicalData) {
  // Create company with historical date
  const result = await os.onboard({
    externalOrgId: org.id,
    label: "Imported", // Display label for historical import
    hints: {
      companyName: org.name,
      domain: org.domain,
    },
    tierCode: org.plan,
    contactEmail: org.adminEmail,
    createdAt: org.createdAt, // Historical timestamp
  })

  // Sync contacts
  await os.contacts.bulkUpsert({
    companyId: result.companyId,
    contacts: org.users.map((u) => ({
      email: u.email,
      name: u.name,
      role: u.role,
    })),
  })
}

类型脚本支持

SDK完全打入了全面的TypeScript定义.

导入类型

import { ChatarminOS } from "@chatarmin/os"
import type {
  ChatarminOSConfig,
  CreateCompanyInput,
  ContactInput,
  FeatureCheckInput,
  FeatureSetAccessInput,
  TrackUsageInput,
  OnboardInput,
  OnboardResult,
  ApplyTierInput,
  ClaimCheckoutInput,
  LinkSubscriptionInput,
} from "@chatarmin/os"

路由器类型

import type { AppRouter } from "@chatarmin/os"

配置

选项类型需求默认说明
apiKeystring✅—— 说OS 管理员面板的 API 密钥
baseUrlstring—— 说https://os.chatarmin.com/api/v1API 终点
const os = new ChatarminOS({
  apiKey: process.env.OS_API_KEY!,
  baseUrl: "https://os.chatarmin.com/api/v1", // Default
})

// Local development
const osLocal = new ChatarminOS({
  apiKey: process.env.OS_API_KEY!,
  baseUrl: "http://localhost:3000/api/v1",
})

获取您的 API 密钥

  1. (原始内容存档于2017-10-03) (英语). Go to ChatarminOS ~ 设置 → 开发者 → API 密钥
  2. 点击“ 创建 API 密钥 ”
  3. 复制密钥(格式 : os_sk_xxxxxxxxxxxx(中文(简体) )
  4. 在环境变量中存储

处理错误

SDK在引擎盖下使用tRPC. 错误包括结构化信息 :

try {
  await os.billing.trackUsage({
    companyId: "comp_xxx",
    featureCode: "ai_credit",
    quantity: 1000,
  })
} catch (error) {
  if (error instanceof TRPCClientError) {
    console.error("Code:", error.data?.code) // e.g., 'FORBIDDEN'
    console.error("Message:", error.message) // Human-readable message
    console.error("HTTP Status:", error.data?.httpStatus)
  }
}

常见错误代码

代码说明
UNAUTHORIZED无效或缺失的 API 密钥
FORBIDDEN没有访问此资源
NOT_FOUND未发现公司/地貌
BAD_REQUEST无效的输入参数
PRECONDITION_FAILED超过使用限额

  • 产品计费API 计费读取API, externalOrgId 范围界定和预览测试租户
  • 变动情况 ——发行笔记. 下注 ## Unreleased 当它到达时释放自己 main (单位:千美元).github/workflows/sdk-release.yml: (major) 编号 : (minor) 在它的头上取出起起起落, 否则是一个补丁
  • 测试-sdk.ts ——完整用法示例

支助


许可证

麻省理工学院

版本与发布记录

@chatarmin/os 共发布 62 个版本,首次发布于 2026/01/08;过去 12 个月发布了 62 个版本。

版本发布日期距今
1.44.0latest2026/11/081 天前
1.43.02026/11/063 天前
1.42.02026/11/053 天前
1.41.02026/11/036 天前
1.40.02026/10/319 天前
1.39.02026/10/2910 天前

关于 @chatarmin/os 的常见问题

@chatarmin/os 是什么?

ChatarminOS 的 SDK 类型安全 - 客户和订阅管理

如何安装 @chatarmin/os?

在项目目录运行 npm install @chatarmin/os,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:typescript(^5.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@chatarmin/os 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@chatarmin/os 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@chatarmin/os 可以商用吗?

@chatarmin/os 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@chatarmin/os 的下载量有多少?

根据 npm 官方下载统计,@chatarmin/os 在 2026/10/10 – 2026/11/08 这 30 天内被下载 26,381 次,日均约 879 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@chatarmin/os 还在维护吗?

最新版本 1.44.0 发布于 2026/11/08(1 天前);至今共发布 62 个版本,其中过去 12 个月发布了 62 个;首次发布于 2026/01/08。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。