{"_id":"@sume-com/sdk","_rev":"3-b750595711c04c662d972dcfa1cd3bcf","name":"@sume-com/sdk","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@sume-com/sdk","version":"0.1.0","keywords":["sume","api","sdk","formats","agents","webhooks"],"license":"MIT","_id":"@sume-com/sdk@0.1.0","maintainers":[{"name":"dev-dooi","email":"dev@sume.com"},{"name":"chasehuh","email":"chase@sume.com"}],"homepage":"https://docs.sume.com/public-api","bugs":{"url":"https://github.com/sumelabs/sume-com/issues"},"dist":{"shasum":"ba0b6a504087b2b894fc55cc37cb77822f31db1d","tarball":"https://registry.npmjs.org/@sume-com/sdk/-/sdk-0.1.0.tgz","fileCount":62,"integrity":"sha512-LgYwT986J7P18lehH3ktdZf0y6ovlaDTRjmp6IcB8NYxPvUxOGwwuYXCNnGjcTZOFhYspCrHun8Y3xNxYbSDCw==","signatures":[{"sig":"MEUCIQDTtYYJNPLGB0OvWwU6MhFHd92dF/My3cs5pQRdXD4m+AIgB5Yp3HNDnxnrmvgRmzNuA/0wtp3JO3aLceUJ80gWgiQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":754570},"type":"module","exports":{".":"./src/index.ts"},"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","check":"node scripts/check-generated.mjs","generate":"openapi-ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"chasehuh","email":"chase@sume.com"},"repository":{"url":"git+https://github.com/sumelabs/sume-com.git","type":"git","directory":"packages/sdk"},"_npmVersion":"10.9.8","description":"TypeScript client for the Sume HTTP API, with run polling and webhook verification helpers.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.2","publishConfig":{"main":"./dist/index.js","types":"./dist/index.d.ts","access":"public","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@hey-api/openapi-ts":"0.98.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0_1785671189008_0.42994675567882545","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@sume-com/sdk","version":"0.1.3","keywords":["sume","api","sdk","formats","agents","webhooks"],"license":"MIT","_id":"@sume-com/sdk@0.1.3","maintainers":[{"name":"dev-dooi","email":"dev@sume.com"},{"name":"chasehuh","email":"chase@sume.com"}],"homepage":"https://docs.sume.com/public-api","bugs":{"url":"https://github.com/sumelabs/sume-com/issues"},"dist":{"shasum":"6096a0f2a5e3eb277120e0743b6995e37aebb945","tarball":"https://registry.npmjs.org/@sume-com/sdk/-/sdk-0.1.3.tgz","fileCount":63,"integrity":"sha512-sdVLBnr6FMfptoFWyzM6UeUM34aQK0XOLsq14mDai/KEDCSsDXR1EdNWhiCUA9EMjnG/MyDK6R+ghFlKF3plHw==","signatures":[{"sig":"MEQCIH3fBaiwGQhZicH4xGkmwWh7Pb1mPCgZkYV+kEs1GaDWAiBCYkDTuL1uO3w/m+FDb6lu0BeMUaxCZuv14vDiEaMnLg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":757235},"main":"./dist/index.js","type":"module","_from":"file:/home/runner/work/_temp/sdk-pack/sume-com-sdk-0.1.3.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","check":"node scripts/check-generated.mjs","generate":"openapi-ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:568587ca-f4ef-429e-920e-90ea2b6487aa"}},"_resolved":"/home/runner/work/_temp/sdk-pack/sume-com-sdk-0.1.3.tgz","_integrity":"sha512-sdVLBnr6FMfptoFWyzM6UeUM34aQK0XOLsq14mDai/KEDCSsDXR1EdNWhiCUA9EMjnG/MyDK6R+ghFlKF3plHw==","repository":{"url":"git+https://github.com/sumelabs/sume-com.git","type":"git","directory":"packages/sdk"},"_npmVersion":"11.19.0","description":"TypeScript client for the Sume HTTP API, with run polling and webhook verification helpers.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@hey-api/openapi-ts":"0.98.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.3_1785674588251_0.6012782141201716","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@sume-com/sdk","version":"0.2.0","description":"TypeScript client for the Sume HTTP API, with run polling and webhook verification helpers.","license":"MIT","keywords":["sume","api","sdk","formats","agents","webhooks"],"repository":{"type":"git","url":"git+https://github.com/sumelabs/sume-com.git","directory":"packages/sdk"},"bugs":{"url":"https://github.com/sumelabs/sume-com/issues"},"homepage":"https://docs.sume.com/public-api","type":"module","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"publishConfig":{"access":"public"},"devDependencies":{"@hey-api/openapi-ts":"0.98.2","typescript":"^5.9.3"},"scripts":{"generate":"openapi-ts","check":"node scripts/check-generated.mjs","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","test":"vitest run"},"main":"./dist/index.js","types":"./dist/index.d.ts","_id":"@sume-com/sdk@0.2.0","_integrity":"sha512-dKwkwRUxIHufKZVhhC0YmB7OJPfVag8bWV4YXPEXH40BB+QlcX8tJFHHu0n5mVdc3r5bL6dfNl8oJVP1S53QHQ==","_resolved":"/home/runner/work/_temp/sdk-pack/sume-com-sdk-0.2.0.tgz","_from":"file:/home/runner/work/_temp/sdk-pack/sume-com-sdk-0.2.0.tgz","_nodeVersion":"22.23.1","_npmVersion":"11.19.0","dist":{"integrity":"sha512-dKwkwRUxIHufKZVhhC0YmB7OJPfVag8bWV4YXPEXH40BB+QlcX8tJFHHu0n5mVdc3r5bL6dfNl8oJVP1S53QHQ==","shasum":"f91f3c37f951187947706cf2a940a0017a0e447d","tarball":"https://registry.npmjs.org/@sume-com/sdk/-/sdk-0.2.0.tgz","fileCount":72,"unpackedSize":786707,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCkq9gYwIXOqhqho4vJvH4GDgpgVO0lfbPspxtjrtL+WQIgTq5o6TY1A6d2TjoF7we5cacJ3bOqB2RXxS1EJ4ZjnrE="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:568587ca-f4ef-429e-920e-90ea2b6487aa"}},"directories":{},"maintainers":[{"name":"dev-dooi","email":"dev@sume.com"},{"name":"chasehuh","email":"chase@sume.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.2.0_1785678170956_0.9783914735124328"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-02T11:46:28.748Z","modified":"2026-08-02T13:42:51.334Z","0.1.0":"2026-08-02T11:46:29.141Z","0.1.3":"2026-08-02T12:43:08.417Z","0.2.0":"2026-08-02T13:42:51.114Z"},"bugs":{"url":"https://github.com/sumelabs/sume-com/issues"},"license":"MIT","homepage":"https://docs.sume.com/public-api","keywords":["sume","api","sdk","formats","agents","webhooks"],"repository":{"type":"git","url":"git+https://github.com/sumelabs/sume-com.git","directory":"packages/sdk"},"description":"TypeScript client for the Sume HTTP API, with run polling and webhook verification helpers.","maintainers":[{"name":"dev-dooi","email":"dev@sume.com"},{"name":"chasehuh","email":"chase@sume.com"}],"readme":"# `@sume-com/sdk`\n\nTypeScript client for the [Sume](https://docs.sume.com/public-api) HTTP API. Every operation in the public OpenAPI schema, plus the helpers every partner otherwise writes by hand: `subscribeFormatRun`, `uploadFile`, `waitForRun`, and `verifyWebhook`.\n\n```bash\nnpm install @sume-com/sdk\n```\n\nRequires a runtime with `fetch` and WebCrypto — Node 18+, Bun, Deno, Cloudflare Workers, or a browser. The package has no runtime dependencies.\n\nPublished on npm as [`@sume-com/sdk`](https://www.npmjs.com/package/@sume-com/sdk), MIT licensed.\n\nFull docs: **<https://docs.sume.com/sdk>**. This README covers the same surface plus how the package is generated, gated, and published.\n\n## Usage\n\n```ts\nimport { createSumeClient, listFormats } from \"@sume-com/sdk\";\n\nconst client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });\nconst { data } = await listFormats({ client });\n```\n\nAuth defaults to `x-api-key` only (scheme-aware); bearer `Authorization` is not set.\n\n**Server-side only.** A Sume API key spends your credits. Never ship one to a browser or a mobile bundle — see [Embed a Format in your product](https://docs.sume.com/cookbooks/embed-a-format).\n\n### Wait for a run\n\nFormat, Action, and Agent Completion runs are asynchronous. `waitForRun` polls `status_url` to a terminal status and then resolves with the full receipt.\n\n```ts\nimport {\n  createSumeClient,\n  createFormatRunByVanityPath,\n  waitForRun,\n} from \"@sume-com/sdk\";\n\nconst client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });\n\nconst { data: created } = await createFormatRunByVanityPath({\n  client,\n  path: { handle: \"acme\", slug: \"product-promo\" },\n  body: { input: { product_url: \"https://shop.example.com/p/8823\" } },\n});\n\nconst run = await waitForRun(created!.data.id, {\n  client,\n  family: \"format\",\n  timeout: 15 * 60_000,\n  pollInterval: 2_000,\n  signal: AbortSignal.timeout(20 * 60_000),\n  onStatus: (status) => console.log(status),\n});\n\nif (run.status === \"completed\") console.log(run.primary_output_url);\n```\n\n| Option         | Default        | Notes                                                                                                                                                               |\n| -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `family`       | —              | Required: `\"action\" \\| \"format\" \\| \"agent\"`. A run id does not say which surface it belongs to, so it cannot be inferred.                                           |\n| `client`       | module default | The client from `createSumeClient()`.                                                                                                                               |\n| `timeout`      | 10 min         | Exceeding it throws `SumeRunTimeoutError`. The deadline is checked _before_ sleeping, so a short timeout does not first wait out a poll interval.                   |\n| `pollInterval` | 2 s            | Gap between status reads.                                                                                                                                           |\n| `signal`       | —              | Aborts the wait and the in-flight request. Rejects with the signal's reason.                                                                                        |\n| `onStatus`     | —              | Called on every status read, including the terminal one. Receives `(status, snapshot)`; the snapshot adds `next_action`, `started_at`, `finished_at`, `cancelable`. |\n\nIt resolves for **any** terminal status, not just `completed` — a failed run is a result, so read `run.status` and `run.error` exactly as a webhook handler would. An API error (401, 404, 5xx) throws `SumeRunRequestError` carrying `status` and `body`.\n\nPrefer a webhook where you can: `waitForRun` is a poll loop, and [run webhooks](https://docs.sume.com/agents/run-webhooks) deliver the identical receipt without one.\n\n### One call: create and wait\n\n`subscribeFormatRun` is `createFormatRun*` plus `waitForRun` in one call. It takes either a vanity path or an opaque `format_id`, forwards `Idempotency-Key`, and resolves with the terminal receipt.\n\n```ts\nimport { createSumeClient, subscribeFormatRun } from \"@sume-com/sdk\";\n\nconst client = createSumeClient({\n  apiKey: process.env.SUME_API_KEY!,\n  baseUrl: \"https://api.dev.sume.com\", // prod default is https://api.sume.com\n});\n\nconst run = await subscribeFormatRun({\n  client,\n  path: { handle: \"acme\", slug: \"product-promo\" },\n  idempotencyKey: `order-${orderId}`,\n  body: { input: { product_url: \"https://shop.example.com/p/8823\" } },\n  onStatus: (status, snapshot) => console.log(status, snapshot.next_action),\n});\n\nif (run.status === \"completed\") console.log(run.primary_output_url);\n```\n\n- Default `timeout` is **20 minutes**, not `waitForRun`'s 10 — video Formats routinely run 10–20.\n- Resolves for any terminal status, exactly like `waitForRun`. It **throws** only when the create call itself is refused, since there is no run to wait for.\n- An idempotent replay of an already-finished run returns immediately without polling.\n- There is no event stream to subscribe to. `events_url` is `null` on every run today, so `onStatus` reflects status polling — real, but not a log feed. `next_action` is the field worth branching on: it separates `poll_status` from `fix_input`.\n\n### Live Commerce, end to end\n\nThe full partner path: upload optional imagery, invoke a team Format, read one durable video URL.\n\n```ts\nimport {\n  createSumeClient,\n  subscribeFormatRun,\n  uploadFile,\n  LIVE_COMMERCE_OUTPUT_SCHEMA,\n  LIVE_COMMERCE_PRIMARY_OUTPUT_KEY,\n  type LiveCommerceFormatInput,\n} from \"@sume-com/sdk\";\n\nconst client = createSumeClient({\n  apiKey: process.env.SUME_TEAM_API_KEY!, // team key — see below\n  baseUrl: \"https://api.dev.sume.com\",\n});\n\n// Optional: your own imagery, uploaded to a durable Sume URL.\nconst { url } = await uploadFile({\n  client,\n  file: await fs.openAsBlob(\"./hero.png\"),\n  filename: \"hero.png\",\n});\n\nconst input: LiveCommerceFormatInput = {\n  product_url: \"https://shop.example.com/p/8823\",\n};\n\nconst run = await subscribeFormatRun({\n  client,\n  path: { handle: \"mobidoo\", slug: \"live-commerce\" },\n  idempotencyKey: `order-${orderId}`,\n  body: {\n    input: { ...input, hero_image_url: url },\n    generation_spend_cap_usd: 3,\n    output_schema: LIVE_COMMERCE_OUTPUT_SCHEMA,\n    primary_output_key: LIVE_COMMERCE_PRIMARY_OUTPUT_KEY,\n    // Or skip the wait entirely and take the webhook:\n    // communication: { webhook_url: \"https://partner.example/hooks/sume\" },\n  },\n});\n\nif (run.status === \"completed\") {\n  console.log(run.primary_output_url);\n  console.log(run.output.live_commerce_video.url); // same URL, named\n}\n```\n\n#### Naming the output, or not\n\nA Format declares no output schema of its own, so this is a choice you make per call:\n\n|             | Request                                     | Read the video at                |\n| ----------- | ------------------------------------------- | -------------------------------- |\n| **Default** | omit `output_schema`                        | `output.videos[0].url`           |\n| **Named**   | pass `output_schema` + `primary_output_key` | `output.live_commerce_video.url` |\n\n`primary_output_url` is set either way. Passing `primary_output_key: \"live_commerce_video\"` **without** a schema does nothing: the key has to exist in the projected output to be selected, and the default projection only ever contains `text`, `images`, `videos`, `audio`, and `files`.\n\nPrefer the named form. If a run produces no video, a named schema fails loudly with `output_schema_unsatisfied`, whereas the default projection quietly returns `videos: []` and `output_error: null`.\n\n### Team Formats need a team key\n\nA Format owned by a team workspace can only be invoked with an API key **issued in that workspace**. Being a member of the team is not enough — a personal key is refused:\n\n```json\n{\n  \"error\": {\n    \"code\": \"workspace_key_required\",\n    \"message\": \"This Format belongs to a team workspace...\",\n    \"details\": { \"workspace_id\": \"org_...\" }\n  }\n}\n```\n\nThis is a `403`, and it is deliberate. A team Format's runs are billed to the team wallet, counted against the team's quota, and read back through the team's workspace. A personal key would put the spend on your personal wallet while the run belonged to the team — which, before this was enforced, produced runs that generated a real video and then reported `output_schema_unsatisfied` because the team-scoped harvest could not see media filed under a personal wallet.\n\nSo: create the key from the team's dashboard, not your own. A personal key remains correct for your own personal Formats.\n\nA team handle you are _not_ a member of returns `404`, indistinguishable from one that does not exist.\n\n### Upload a file\n\n`uploadFile` reserves a presigned URL, PUTs the bytes straight to storage, and completes the asset, resolving with a **durable** HTTPS URL you can pass as Format `input`.\n\n```ts\nconst { url, asset_id } = await uploadFile({\n  client,\n  file: new Blob([bytes], { type: \"image/png\" }),\n  filename: \"hero.png\",\n});\n```\n\n- `contentType` is required unless `file` is a `Blob` carrying a type.\n- Failures throw `SumeUploadError` with `step` set to `\"create\" | \"put\" | \"complete\"`.\n- The bytes never pass through the Sume API, so upload speed is between you and storage.\n- The PUT reuses the `fetch` you gave `createSumeClient`, so a proxied client stays proxied.\n\n### Verify a webhook\n\n```ts\nimport { verifyWebhook } from \"@sume-com/sdk\";\n\nexport async function POST(request: Request) {\n  const body = await request.text(); // raw, before any JSON.parse\n\n  const ok = await verifyWebhook({\n    body,\n    headers: request.headers,\n    secret: process.env.SUME_WEBHOOK_SECRET!,\n  });\n  if (!ok) return new Response(\"bad signature\", { status: 401 });\n\n  const event = JSON.parse(body);\n  await recordTerminalRun(event.request_id, event); // dedupe on request_id\n  return new Response(null, { status: 204 }); // fast 2xx, then work\n}\n```\n\n- **Async**, because it uses WebCrypto rather than `node:crypto` — that is what keeps the package importable from Workers, Deno, and bundlers that refuse `node:` specifiers.\n- Pass the **raw** body. A parsed-and-reserialized object does not verify; key order and whitespace are part of what was signed. In Express, mount `express.raw({ type: \"application/json\" })` on the webhook route only.\n- `headers` accepts a `Headers`, a `Map`, or a plain object (Node's `req.headers`), and is case-insensitive.\n- Returns `false` rather than throwing on a malformed delivery — a missing header is a failed verification, which is what you want to branch on.\n- `toleranceSeconds` defaults to 300. Set `0` to skip the replay-window check.\n\nOne verifier covers both surfaces: **run** webhooks (`*.run.terminal`) and generation-**job** webhooks (`job.*`) share the `sume-v1` HMAC-SHA256 scheme over `<timestamp>.<raw_body>`. The payloads differ; the signature does not. Route on `event`.\n\n## Generate\n\nRegenerate from the committed OpenAPI snapshot:\n\n```bash\n# Rebase on origin/main first — local checkouts can lag behind the 78-path schema.\npnpm --filter @sume-com/sdk generate\n```\n\nInput is always `apps/docs/public/api/openapi.json` (relative from this package). There is no live-URL mode in the generator config.\n\n## Drift gate\n\n`src/generated` is committed, so it can go stale when the snapshot moves. CI runs:\n\n```bash\npnpm --filter @sume-com/sdk check\n```\n\nThis regenerates into a temporary directory and compares — it never touches your working tree — and fails if any file was added, removed, or changed. The fix is always `pnpm --filter @sume-com/sdk generate`, then commit the result.\n\nSnapshot-sync PRs from `sync-docs-openapi-snapshot.yml` regenerate the client in the same commit, so they stay green without manual work.\n\n`src/create-client.ts`, `src/wait-for-run.ts`, and `src/verify-webhook.ts` are hand-written and untouched by the generator.\n\n### Manual live-drift probe\n\n```bash\nnpx @hey-api/openapi-ts@0.98.2 -i https://api.sume.com/reference/json -o /tmp/sdk-drift -c @hey-api/client-fetch\n```\n\n## Publishing\n\nWorkspace consumers resolve `exports` straight to `src/index.ts`; `publishConfig` swaps that for `dist` at publish time, so npm consumers get compiled JS and `.d.ts` without the workspace needing a build step.\n\n`tsc` copies import specifiers into `dist` verbatim, so every relative import — hand-written and generated — carries an explicit `.js` extension. Without it `node` rejects `dist/index.js` with `ERR_UNSUPPORTED_DIR_IMPORT`, which is what burned `0.1.1`. Two things hold the line: this package typechecks under `moduleResolution: nodenext`, which makes an extensionless relative import a compile error, and `openapi-ts.config.ts` sets `output.module.extension` so the generated tree matches.\n\n`.github/workflows/sdk-release.yml` runs the drift gate, typecheck, tests, build, and a tarball import smoke test, then publishes:\n\n```bash\ngit tag sdk-v0.2.0 && git push origin sdk-v0.2.0\n```\n\nBump `version` in `package.json` on `main` before tagging; the workflow refuses a tag that disagrees with the manifest.\n\n`workflow_dispatch` defaults to `dry_run: true` — it packs and stops.\n\n### Trusted Publishing\n\nAuthentication is [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/) over OIDC. There is no `NPM_TOKEN`, and no long-lived credential to rotate: npm mints a short-lived token for the run after checking the request came from `sumelabs/sume-com` running `sdk-release.yml`.\n\nThree constraints follow from that, all encoded in the workflow:\n\n- **Do not rename `sdk-release.yml`.** The trusted publisher is bound to the workflow filename; renaming it breaks publishing until the npm UI is updated to match. Same for moving the publish step into a reusable workflow.\n- **The publish job must stay on a GitHub-hosted runner.** OIDC is not supported from self-hosted runners, so this workflow pins `ubuntu-latest` instead of the monorepo's `CI_RUNS_ON`.\n- **`pnpm` packs, `npm` publishes.** `publishConfig` field overrides are a pnpm feature that npm does not apply, and pnpm cannot do the OIDC exchange — so `pnpm pack` builds the tarball and `npm publish <tarball>` authenticates. npm must be ≥ 11.5.1, newer than what Node 22 bundles.\n\nProvenance attestations are disabled (`NPM_CONFIG_PROVENANCE=false`): npm cannot attest builds from a private source repo.\n","readmeFilename":"README.md"}