Partner API
AI index
Launch contract: sandbox is synthetic and cannot charge or create a patient. Use only signed-onboarding origins and credentials.

@chiahealth/partner

Server-only TypeScript SDK for the Chia Partner Care integration.

It has no executable runtime dependencies, does not include telemetry, supports exact onboarding-supplied API origins, refuses to co-locate runtime and finance keys, redacts credentials/context/idempotency values from errors, and provides:

  • role-narrowed runtime and finance clients;
  • consent-gated short-lived sandbox and production handoff creation;
  • idempotent synthetic webhook tests;
  • authenticated streaming asset verification with commit/abort sinks;
  • strict endpoint-ownership challenge proof generation;
  • exact-raw-body HMAC verification of one sender signature against the active and optional retiring receiver secret;
  • a test-first webhook router;
  • local hidden / neutral_only / share_or_neutral resolution.

This package is never a mobile or browser SDK. Importing it into a browser/mobile bundle is unsupported and the runtime guard fails closed.

Catalog decoding also fails closed unless the credential-bound partner_id is a canonical opaque key: 2–32 characters matching ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$. Treat that value as data; never derive a hostname, storage name, secret path, or authorization rule from it.

After Chia marks a release supported in a signed onboarding receipt, the public documentation origin must publish the same deterministic package bytes named by that receipt. Start from /sdk-release.json at the exact HTTPS documentation origin Chia supplied, validate its content-addressed URL, then verify before installing. A missing or mismatched document is a hard stop, not a reason to use an unpinned package:

DOCS_ORIGIN="${CHIA_PARTNER_DOCS_ORIGIN:?set the bare HTTPS docs origin Chia supplied}"
test "${DOCS_ORIGIN#https://}" != "$DOCS_ORIGIN"
test "${DOCS_ORIGIN%/}" = "$DOCS_ORIGIN"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error \
  "$DOCS_ORIGIN/sdk-release.json" \
  --output chia-partner-sdk-release.json
sdk_sha="$(jq -er '.artifact.sha256 | select(test("^[0-9a-f]{64}$"))' \
  chia-partner-sdk-release.json)"
sdk_url="$(jq -er '.artifact.url' chia-partner-sdk-release.json)"
test "$sdk_url" = \
  "$DOCS_ORIGIN/downloads/sdk/$sdk_sha/chiahealth-partner-2.1.0.tgz"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error \
  "$sdk_url" --output chiahealth-partner-2.1.0.tgz
python3 - "$sdk_sha" chiahealth-partner-2.1.0.tgz <<'PY'
import hashlib, hmac, pathlib, sys

expected = sys.argv[1]
actual = hashlib.sha256(pathlib.Path(sys.argv[2]).read_bytes()).hexdigest()
if not hmac.compare_digest(actual, expected):
    raise SystemExit("SDK checksum mismatch")
PY
npm install --save-exact --ignore-scripts --no-audit --no-fund \
  ./chiahealth-partner-2.1.0.tgz

This public artifact contains compiled server code and declarations only: no credentials, partner configuration, telemetry, install scripts, or patient/consumer data. Before sandbox certification or production use, also verify that its SHA-256 is the exact SDK digest in Chia's authenticated onboarding release manifest. Do not run an unpinned registry install.

import {
  createFinancePartnerClient,
  createRuntimePartnerClient,
} from "@chiahealth/partner";

const runtime = createRuntimePartnerClient({
  environment: "sandbox",
  // Required exact non-secret API origin from signed onboarding.
  apiOrigin: process.env.CHIA_API_ORIGIN!,
  // Exact non-secret authenticated asset origin; strict SSRF allowlist only.
  assetOrigin: process.env.CHIA_ASSET_ORIGIN!,
  runtimeKey: process.env.CHIA_RUNTIME_KEY!,
  // Exact non-secret origin from the sandbox onboarding package.
  careOrigin: process.env.CHIA_CARE_ORIGIN!,
});

const catalog = await runtime.listCatalog();
if (!catalog.notModified) {
  await catalogStore.replace(catalog.data); // private backend cache, not logs
}

Client construction fails immediately unless apiOrigin is the exact public HTTPS origin from signed onboarding. listCatalog() also fails closed unless assetOrigin and careOrigin are configured. Every catalog, handoff, settlement, and webhook-test success body is decoded as a closed schema. The authenticated catalog URL must use the exact assetOrigin and /a/<ast> path; it is never constructed. The neutral URL must use careOrigin and the environment path (/sandbox/start or /quiz); a created handoff URL must use apiOrigin and /<environment>/handoff/<phs>.

After rendering the exact catalog disclosure and receiving an affirmative user action, create the environment-matching handoff from the partner's backend. The SDK rejects unknown request fields, so partner user/account/device/contact/state identifiers cannot be added accidentally:

const created = await runtime.createHandoffSession({
  catalogItemId: item.id,
  disclosureVersion: item.handoff.version,
  consentAttestation: {
    affirmed: true,
    affirmedAt: new Date().toISOString(),
  },
  idempotencyKey: persistedRandomUuidV4,
});
// Send only created.data.url and created.data.expires_at to the initiating app response.

The returned URL lasts 15 minutes. Never log, persist, prefetch, analyze, or look it up later; no status endpoint exists. In production it opens Chia's live co-branded identity, intake, eligibility, checkout, payment, care, support, and fulfillment journey. Chia is merchant of record, and no individual result returns to the partner.

Use a separate client/configuration for finance:

const finance = createFinancePartnerClient({
  environment: "sandbox",
  apiOrigin: process.env.CHIA_API_ORIGIN!,
  financeKey: process.env.CHIA_FINANCE_KEY!,
});
const statements = await finance.listSettlementSummaries({ limit: 10 });

Production rows are immutable Finance releases, newest first. Persist them by id; a row with revision > 1 points to the immediately prior release through supersedes_id. Never sum a release after a later revision supersedes it. Amounts and paid/due state are Finance-authored release values, not activity estimates. aggregate_activity can be null; a visible count has already passed Chia's internal threshold and composition release checks, while a suppressed aggregate carries a null count. The SDK rejects a production threshold below the hard floor of 10; a partner-specific approval may require a larger group.

The role factories are preferred over the backwards-compatible ChiaPartnerClient constructor: TypeScript removes finance methods from a runtime client and runtime methods from a finance client, making credential misuse a compile-time error.

Safe requests retry transient network, timeout, 429, and 5xx origin/edge failures up to three attempts. Exponential delays use jitter; a valid integer Retry-After is honored when it does not exceed maxRetryDelayMs (30 seconds by default). A longer requested delay is returned immediately as ChiaPartnerError.retryAfterMs so the caller can move work to a durable queue instead of holding an application request open. Every operation accepts an AbortSignal. timeoutMs is a per-attempt deadline (10 seconds by default) that stays active from transport start through response headers and complete, bounded body consumption. Retries get a fresh per-attempt deadline; the caller's AbortSignal is the cancellation boundary for the complete operation, including retry backoff. Every successful API response must carry the three contract rate-limit headers; the SDK validates and exposes them as typed metadata.

Resolve display/share behavior from the exact publication policy and partner-owned service-state evidence. The resolver returns hidden, neutral_only, or share_or_neutral; it never sends the state to Chia. A live publication may explicitly allow an unknown/stale state to remain visible only with its Chia-eligibility disclosure and a neutral Chia route—never a selection-sharing handoff:

const decision = resolveCatalogHandoff(item, {
  stateCode: "CA",
  evidenceBasis: "partner_account_service_state",
  observedAt: new Date(),
});

For the initial endpoint-ownership check, branch on the dedicated header before signed-event handling. The SDK validates Chia's exact canonical body and returns the required response-header value. This unsigned probe must have no dedupe, queue, catalog, finance, care, or other business side effect:

if (request.headers.has("Chia-Endpoint-Verification-Challenge")) {
  const proof = createPartnerWebhookEndpointProof(rawBody, request.headers);
  return new Response(null, {
    status: 200,
    headers: {
      "Cache-Control": "no-store",
      "Chia-Endpoint-Verification": proof,
    },
  });
}

For signed webhook events, preserve the exact request bytes, verify before parsing or routing, durably deduplicate the event ID, and keep the synthetic branch first:

const event = verifyPartnerWebhook(rawBody, request.headers, {
  environment: "sandbox",
  secrets: [process.env.CHIA_WEBHOOK_SECRET_CURRENT!],
});
await durableEventStore.processOnce(event.id, () =>
  routeVerifiedPartnerWebhook(event, {
    onTest: async () => undefined,
  }),
);

Partner API v2 emits only explicitly requested test:true events, including from the production signer; the webhook signature format is independently versioned as v1=…. onTest must have no cache, finance, care, or analytics side effect. durableEventStore.processOnce commits only the unique synthetic receipt. Frameworks must disable automatic JSON parsing on this route so rawBody remains byte-identical.

The complete runnable reference includes a capped raw-body reader, file-backed demo deduplication, ETag refresh, a commit/abort filesystem asset sink, local availability resolution, isolated finance execution, and all three synthetic webhook tests in both environments. Open the Reference backend guide from the human entrypoint in /sdk-release.json. Replace its single-process file store with the integrating partner's transactional database boundary in production.

The installable tarball has no runtime dependencies and exposes generated declarations from dist/index.d.ts; it does not make consumers compile Chia's TypeScript source. For an optional source audit from the complete integration bundle (not required to integrate the packed SDK), install the pinned development toolchain and run:

bun install --frozen-lockfile --ignore-scripts
bun run check

The package records the exact current OpenAPI, AsyncAPI, asset-contract, and machine-manifest SHA-256 values in PARTNER_CONTRACTS. bun run build deletes dist/ before compiling and fails if the release output contains anything except the expected entrypoint and declarations, so removed entrypoints cannot leak into a package tarball.