@growstack / sdk
整个Growstack公共API的类型化客户端。MIT授权,ESM带有TypeScript声明,使用global fetch 在Node 20+, Bun, Vercel和edge运行时。
npm install @growstack/sdk
import { createGrowstack, GrowstackError } from '@growstack/sdk'
const growstack = createGrowstack({ apiKey: process.env.GROWSTACK_API_KEY! })
// For self-hosting: createGrowstack({ apiKey, baseUrl: 'https://marketing.example.com' })
每个实例都有自己的键、基URL和可选 fetch(request) 实现。在服务器上使用密钥。公钥只属于工作区跟踪策略所允许的浏览器操作。
在已登录的浏览器中,省略 apiKey 并使用应用程序的起源。SDK发送no
授权头并通过Fetch保存cookie credentials 选项,
默认为 same-origin. 集 credentials: 'include' 当你部署的时候
允许来自其他来源的cookie认证请求。
const personal = createGrowstack({ baseUrl: window.location.origin })
const accounts = await personal.connections.list()
连接和成员连接摘要需要会话。API密钥不能 访问它们。
发送事务性消息
需要 messages:send密钥上的授权发送方,以及经过验证的发送方域。这个 message 键是工作区发布的Simple消息模板。其他发布的模板键也以同样的方式工作。
const sent = await growstack.messages.create({
body: {
to: '[email protected]',
from: { address: '[email protected]' },
purpose: 'transactional',
template: {
key: 'message',
variables: { subject: 'Your receipt', body: 'Thank you for your order.' },
},
},
}, { idempotencyKey: 'order:example-42:receipt' })
console.log(sent.id)
插入联系人
需要 people:write. 电子邮件或外部标识符查找或创建现有联系人。省略的字段保持不变;相互冲突的身份永远不会悄然合并。
const { person, created } = await growstack.people.upsert({
body: {
identifier: { email: '[email protected]' },
name: 'Ana',
country: 'ES',
source: 'shop',
},
})
同步产品帐户
需要 people:write. 品牌标识符和不可变账户标识符标识
帐户。使用一个计数器或时间戳保持其源版本增加
种类。相等或更旧的版本返回 ignored: true 并保持所有数据完整。
const { account, ignored } = await growstack.productAccounts.upsert({
path: { brand: 'heralded', externalId: 'workspace-42' },
body: {
revision: 5,
name: 'Example workspace',
status: 'paying',
plan: 'Growth',
currency: 'EUR',
mrr: '79.00',
users: [{ identifier: { email: '[email protected]' }, role: 'owner' }],
},
})
省略的字段保持不变。提供的用户替换成员。用户接受
contact-upsert标识符;外部标识符可以包括电子邮件。
冲突的用户保持未链接和 needsReview 是真的。回顾明确
与 productAccountUsers.review,选择 link 或者 keep_apart.
后者保留了产品标识,而不附加冲突的电子邮件。
属性和字段历史
properties 管理的定义 contact, company, deal, lead 和
product_account. personProperties 公开相同的联系人定义。
共享属性只有一个键和类型 appliesTo 对象。
const definitions = await growstack.properties.list({ path: { object: 'contact' } })
const plan = definitions.data.find(property => property.key === 'plan')
const growth = plan?.optionDefinitions?.find(option => option.label === 'Growth')
const contact = await growstack.people.get({ path: { id: person.id } })
console.log(contact.attributes.plan) // Current label, such as 'Growth'
console.log(contact.attributeOptions?.plan) // { id: '<option UUID>', label: 'Growth' }
选择写入接受标签或选项id in attributes. 多选
接受一个有序的标签或id数组。读取保存标签 attributes
和公开 {id, label} 值 attributeOptions,为a使用数组
按其存储顺序多选。不匹配的值保持写入和
省略了 attributeOptions; invalidProperties 命名他们的字段。
重命名选项将保留其id,并将保存的引用解析为旧的
标签。 optionDefinitions 按id更新选项,并可以存档或合并它们。
peopleChanges.list 和 companiesChanges.list 返回字段随
他们的新旧价值观,演员和时间。重写相同的值会产生no
改变。租户导出包括定义和历史记录。
请求双重选择订阅
需要 consent:write. 创建具有双重选择的主题,或使用以这种方式配置的现有主题。
await growstack.topics.create({
body: { key: 'product-updates', name: 'Product updates', doubleOptIn: true },
})
await growstack.subscriptions.create({
body: { address: '[email protected]', topic: 'product-updates', source: 'shop' },
})
这将创建一个挂起的订阅。收件人通过电子邮件托管链接确认;SDK本身从不确认用户的同意。同意证据字段 details 和 occurredAt 需要一个密钥。公钥和已登录会话无法提交它们。
跟踪事件
需要 events:write 在秘钥上。浏览器跟踪使用公钥及其工作区的事件允许列表。
await growstack.track({
body: { event: 'order.completed', email: '[email protected]', data: { order: 'example-42' } },
}, { idempotencyKey: 'order:example-42:completed' })
上传品牌标志
需要 brands:manage. 传递PNG, JPEG或WebP Blob 或者 File,最高可达200kib;
它的MIME类型。客户端发送原始字节和内容类型。
await growstack.brandsLogo.update({
path: { id: brand.id },
body: new Blob([imageBytes], { type: 'image/webp' }),
})
打开客户计费页面
需要一个密钥 billing:write. 该公司必须是帐单客户
而且品牌必须指定给发行公司。客户不需要
发票;然后页面显示一个空列表。测试模式被拒绝。使用HTTPS
返回的URL。返回的链接在五分钟内工作一次;创建它时
客户选择打开账单。
const portal = await growstack.billingPortalSessions.create({
body: {
companyId: customer.companyId,
brandId: brand.id,
returnUrl: 'https://app.example.com/account',
locale: 'en',
},
})
// Redirect your customer to portal.url.
该页面支持英语和罗马尼亚语,显示发票和报价pdf,并接受
发票支付。它不能接受引号。12个空闲小时后结束访问
或者当它的颁发密钥被移除或丢失时 billing:write.
处理错误
try {
await growstack.people.upsert({
body: { identifier: { email: '[email protected]' }, source: 'shop' },
})
} catch (error) {
if (error instanceof GrowstackError) {
console.error(error.status, error.type, error.code, error.message, error.requestId)
// error.issues contains field paths and messages when supplied by the API.
} else {
throw error // Network failures and cancellation retain their original errors.
}
}
API信封之外的错误响应(例如网关的HTML 502)抛出 GrowstackError HTTP状态, type: 'internal', code: 'unexpected_response' 和 requestId: null.
呼叫、重试和取消
方法接受生成的 { path, query, body } 输入后跟可选 { idempotencyKey, signal },并返回解码后的成功消息体。操作使用Growstack的显式操作映射中的资源和操作,例如 growstack.campaigns.send 和 growstack.peopleEmailAddresses.list. growstack.raw 公开每个操作id,例如 raw.peopleUpsert 和 raw.trackCreate,投入和运输方式相同。
每个POST都接收一个自动UUID等幂键,除非您提供一个。生成的密钥保护一次调用内的重试。在表示相同操作的不同调用之间传递您自己的稳定密钥,如收据示例中所示。API的重放窗口是24小时。
由于网络故障,get和键控post在第一次尝试后最多重试三次,分别是429、5xx和409 idempotency_key_in_use. 每次重试都会保留确切的密钥和主体。延迟使用全抖动over min(8000, 500 × 2^retryIndex) 毫秒,或更长的有效时间 Retry-After 以秒为单位或作为HTTP日期。其他4xx,其他409和取消呼叫永远不会重试。PUT、PATCH和DELETE永远不会重试。
const controller = new AbortController()
const result = growstack.people.list({ query: { limit: 100 } }, { signal: controller.signal })
// controller.abort() cancels the request or a retry delay.
await result
迭代记录页
每个返回记录页的操作都有 .all(input, options),包括POST查询和嵌套列表。它每次产生一条记录,保留过滤器和父参数,并在 hasMore 是假的。
for await (const person of growstack.people.list.all({ query: { limit: 100 } })) {
console.log(person.id)
}
for await (const row of growstack.people.query.all({
body: { filter: { person: 'country', op: 'eq', value: 'ES' }, limit: 100 },
})) {
console.log(row.person.id)
}
每个POST页都有自己的键,可用于该页的重试。如果迭代器接收到调用方键,则应用于第一页;后面的页面生成新的键,因为它们的请求体不同。发件箱迭代器 growstack.events.list.all 发送检查点 after 当被追上时就会停止,即使最后一个检查点仍然可用。使用 events.list 直接为以后的投票保存检查点。
开发和发行
服务器拥有MIT公共文档。导出没有工人,连接或凭证,然后生成与固定 @hey-api/openapi-ts 获取客户端:
bun run --cwd apps/server openapi:export
bun run --cwd packages/sdk generate
bun run --cwd packages/sdk build
bun run verify
提交 openapi.json, src/generated 和 src/bindings.gen.ts. 根验证重新导出和重新生成它们,在漂移时失败,然后构建和类型检查SDK。TypeScript 6.0.3是一个本地开发依赖,因为生成器使用了它的编译器API;存储库的TypeScript 7仍然运行根类型检查。
组件创作用途 growstack.components.preview 和 growstack.components.generate.
存储库发布通过创建一个版本 growstack.library.create 或者
growstack.libraryVersions.create,然后呼叫 growstack.libraryVersions.publish. 结果的
approvalId 确定所需的工作空间评审; publishedAt 在获得批准之前保持为空。
存储库的 bun run components CLI使用Bun构建脚本依赖项并使用它们
相同的操作。看到 packages/components/README.md.
bun run version:sdk patch|minor|major|<x.y.z> 更新版本和变更日志。这个 sdk-v<version> 标记通过OIDC触发npm可信发布的发布工作流。监督器配置受信任的发布者并推送初始的 sdk-v0.1.0 标签。如果源存储库是私有的,则省略出处,当它变为公共时必须启用出处。该软件包包含已构建的SDK、MIT源代码和文档,没有生成脚本、服务器代码或测试。源代码支持存储库的 @growstack/source 出口条件。
管道和交易
growstack.deals 创建,读取,查询,编辑,重新打开和删除交易。
growstack.pipelines 和 growstack.pipelineStages 管理管道和阶段;
这些写操作需要所有者或管理会话。舞台剪辑保留了胜利和胜利
失去的类别。交易更新可以提供所需的字段以及 stageId;
expectedStageId 拒绝陈腐的举动。查询响应包括开放和加权
工作区中的汇总报告货币,并确定不可用的汇率。
growstack.dealSettings 读取和更新报告币种和丢失原因。