{"_id":"@burjx/partner-sdk","name":"@burjx/partner-sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@burjx/partner-sdk","version":"1.0.0","description":"BurjX TypeScript SDK for regulated KYC and KYB Journeys, Integration Readiness, Events, and webhook delivery evidence","type":"module","sideEffects":["./dist/browser-guard.js"],"exports":{".":{"types":"./dist/index.d.ts","browser":"./dist/browser-guard.js","import":"./dist/index.js"},"./contracts":{"types":"./dist/contracts.d.ts","import":"./dist/contracts.js"},"./quickstart":{"types":"./dist/quickstart.d.ts","import":"./dist/quickstart.js"},"./webhooks":{"types":"./dist/webhookSignature.d.ts","browser":"./dist/browser-guard.js","import":"./dist/webhookSignature.js"}},"scripts":{"build":"tsc --project tsconfig.build.json","typecheck":"tsc --project tsconfig.json"},"dependencies":{"zod":"4.4.3"},"devDependencies":{"@types/node":"26.1.1"},"engines":{"node":">=22"},"browser":{"./dist/index.js":"./dist/browser-guard.js"},"_id":"@burjx/partner-sdk@1.0.0","_integrity":"sha512-ZWz/IID504a79S/Z4adKAeNev1NrBUwVyayzeRDSuJDcyy0oDrvLHiiU4XXchDkoMaYsp/xrCUP7N/RJGWjNQA==","_resolved":"/tmp/burjx-ticket14-release.3AEXVd/burjx-partner-sdk-1.0.0.tgz","_from":"file:/tmp/burjx-ticket14-release.3AEXVd/burjx-partner-sdk-1.0.0.tgz","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-ZWz/IID504a79S/Z4adKAeNev1NrBUwVyayzeRDSuJDcyy0oDrvLHiiU4XXchDkoMaYsp/xrCUP7N/RJGWjNQA==","shasum":"8157d892134b0c880738853fbb19f4010d715e44","tarball":"https://registry.npmjs.org/@burjx/partner-sdk/-/partner-sdk-1.0.0.tgz","fileCount":80,"unpackedSize":200081,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAcXDZKb1jo6f4mjhpqjvua97BAJdkZ7wwBlFGstXMVVAiA1aRVmjCeYbakle8LjZzj5abFxjoNHw2MtFHRZzIyX6w=="}]},"_npmUser":{"name":"burjx","email":"tech@burjx.com"},"directories":{},"maintainers":[{"name":"burjx","email":"tech@burjx.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/partner-sdk_1.0.0_1785365063547_0.29202352918346786"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-29T22:44:23.365Z","1.0.0":"2026-07-29T22:44:23.697Z","modified":"2026-07-29T22:44:23.918Z"},"maintainers":[{"name":"burjx","email":"tech@burjx.com"}],"description":"BurjX TypeScript SDK for regulated KYC and KYB Journeys, Integration Readiness, Events, and webhook delivery evidence","readme":"# `@burjx/partner-sdk` 1.0\n\nThe server-side TypeScript SDK for BurjX KYC/KYB Journeys, normalized Events,\nand partner webhook delivery evidence. It validates requests and responses at\nruntime and authenticates with an Application-bound Partner API Credential\nusing the OAuth client-credentials grant.\n\nThe package is ESM-first, includes TypeScript declarations, requires Node.js\n22 or newer, and is release-tested on the currently supported Node.js 22, 24,\nand 26 lines. Importing the confidential client or webhook verifier under a\nbrowser export condition fails clearly.\n\n```sh\nnpm install @burjx/partner-sdk\n```\n\n```ts\nimport { createBurjX } from \"@burjx/partner-sdk\";\n\n// The three values BurjX shows you exactly once when it issues an\n// Application-bound Partner API Credential.\nconst clientId = process.env.BURJX_CLIENT_ID;\nconst clientSecret = process.env.BURJX_CLIENT_SECRET;\nconst tokenUrl = process.env.BURJX_TOKEN_URL;\nif (!clientId || !clientSecret || !tokenUrl) {\n  throw new Error(\n    \"BURJX_CLIENT_ID, BURJX_CLIENT_SECRET, and BURJX_TOKEN_URL are required\",\n  );\n}\n\nconst burjx = createBurjX({\n  applicationId: \"app_your_sandbox_web\",\n  credential: { clientId, clientSecret, tokenUrl },\n  environment: \"sandbox\",\n});\n\nexport const createOnboardingLaunch = async () => {\n  const journey = await burjx.journeys.create(\n    {\n      journeyType: \"kyc\", // or \"kyb\"\n      locale: \"en\",\n      partnerSubject: \"your-opaque-customer-reference\",\n      returnUrl: \"https://sandbox.example.com/onboarding/return\",\n    },\n    { idempotencyKey: \"journey-request-018f\" },\n  );\n\n  const launch = await burjx.journeys.createLaunch(\n    journey.journeyId,\n    {\n      mode: \"hosted-link\",\n      parentOrigin: \"https://sandbox.example.com\",\n    },\n    { idempotencyKey: \"launch-request-018f\" },\n  );\n\n  const journeyReadback = await burjx.journeys.get(journey.journeyId);\n  const events = (await burjx.events.list()).filter(\n    (event) => event.journeyId === journey.journeyId,\n  );\n  console.info(\"Authoritative BurjX readback\", {\n    events,\n    journey: journeyReadback,\n  });\n\n  return {\n    expiresAt: launch.expiresAt,\n    journeyId: journey.journeyId,\n    onboardingUrl: launch.onboardingUrl,\n  };\n};\n```\n\nThe SDK acquires the short-lived OAuth token from that credential, caches it in\nmemory, and refreshes it before expiry. It presents the client secret only to\nthe one sandbox authorization server BurjX operates, and refuses any other host,\nso a mistyped or substituted `tokenUrl` cannot leak the secret. Pass\n`onDiagnostic` on the client, or on a single request, to observe each attempt's\noperation, attempt number, correlation ID, HTTP status and duration; the event\nnever carries a secret or a token.\n\n## Environments\n\n`environment: \"sandbox\"` supports all three ways of authorizing: a `credential`,\na `tokenProvider` callback, and a direct `accessToken`.\n\n`environment: \"production\"` supports `tokenProvider` and `accessToken` only.\nPassing a `credential` throws a `BurjXError` with code\n`production_credential_unsupported`, because the production authorization server\nis not approved for this SDK release. Production credentials, production domain\nverification, and go-live are a separate BurjX release decision; until it is\ntaken the SDK refuses rather than guesses where a production client secret\nshould be sent.\n\n## Migrating to 1.0\n\nVersion 1.0 stabilizes the public operation, schema, error, retry, and\nauthentication contracts. See [MIGRATION.md](./MIGRATION.md) for the complete\nupgrade checklist and [CHANGELOG.md](./CHANGELOG.md) for release history. The\ntwo pre-1.0 changes that require action are:\n\n- **`PartnerApiCredential.tokenUrl` is now required.** Earlier builds derived the\n  token endpoint from the API base URL, which the platform does not serve. Use\n  the `tokenUrl` BurjX shows alongside the client id and secret at issuance —\n  the SDK never guesses an authorization server. It must be the approved\n  authorization server origin for the environment; the Partner API host is not\n  a token endpoint and is rejected.\n- **`environment: \"production\"` with a `credential` now throws\n  `production_credential_unsupported`** rather than attempting a token request.\n  See \"Environments\" above.\n\n## Versioning, deprecation, and advisories\n\nThe public package interface follows Semantic Versioning. Backward-compatible\ncapabilities ship in a minor release, backward-compatible fixes in a patch, and\nan intentional breaking interface change requires a new major release.\n\nAn ordinary supported interface will receive a published deprecation notice and\nreplacement path at least six months before removal. BurjX may shorten that\nwindow only for a security or regulatory requirement. An accelerated change\nmust ship with an advisory that identifies the impact, replacement, and\neffective date; it is never applied as an undocumented breaking change.\n\nKeep the root client on your server: the Partner API Credential secret and the\nOAuth token it mints are confidential backend credentials, and importing\n`@burjx/partner-sdk` or `@burjx/partner-sdk/webhooks` in a browser bundle fails\ndeliberately. Return only the launch result from an authenticated backend route.\n\nBrowser-owned code may import the secret-free runtime schemas from\n`@burjx/partner-sdk/contracts` and the dashboard snippet generator from\n`@burjx/partner-sdk/quickstart`. These subpaths expose no client construction,\ntoken acquisition, or webhook-signing functions.\n\n## Journey operations\n\n`burjx.journeys.search()` performs exact, structured lookup inside the\nApplication bound to this client. The server does not accept a per-call\nApplication override, even when a valid sibling Journey ID or Partner Subject\nis known.\n\n```ts\nconst page = await burjx.journeys.search({\n  partnerSubject: \"your-opaque-customer-reference\",\n  journeyType: \"kyc\",\n  status: \"under_review\",\n  order: \"newest\",\n  limit: 50,\n});\n\nfor (const journey of page.data) {\n  console.info(journey.journeyId, {\n    meaning: journey.explanation.meaning,\n    terminal: journey.explanation.terminal,\n    nextActor: journey.explanation.nextActor,\n    safeNextAction: journey.explanation.safeNextAction,\n    authoritativeUpdatedAt:\n      journey.explanation.authoritativeUpdatedAt,\n    reason: journey.explanation.providerIndependentReason,\n  });\n}\n\nconst nextPage = page.nextCursor\n  ? await burjx.journeys.search({\n      partnerSubject: \"your-opaque-customer-reference\",\n      cursor: page.nextCursor,\n      order: \"newest\",\n      limit: 50,\n    })\n  : undefined;\n```\n\nSupported filters are exact Journey ID, exact case-sensitive Partner Subject,\nJourney type, current status, required action, and UTC creation range. Results\nuse filter-bound opaque cursors and stable newest/oldest ordering. Fuzzy,\npersonal-name/email, APEX/provider-identifier, and raw-provider-field search\ndo not exist in the contract. The explanation is the same normalized,\nprovider-independent schema used by OpenAPI and the dashboard.\n\nThe browser receives `onboardingUrl` from that route and passes it unchanged to\n`@burjx/web-sdk`, or the End Client opens it directly. The URL contains a\none-time credential in its fragment: keep it in memory, do not log or persist\nit, and discard it after use or expiry.\n\n`burjx.webhooks.listDeliveries({ limit: 100 })` reads a bounded 1–100 item\nApplication-scoped delivery page within the owning Business Client. Pass its\nopaque `nextCursor` back as `cursor` to continue older evidence without gaps or\nduplicates. Calling\n`burjx.webhooks.replayDelivery(deliveryId, { idempotencyKey })` creates a new\ndelivery attempt for the same stable Event. Reuse that key to converge an\nambiguous request within 24 hours. Delivery evidence includes the Delivery ID,\nstable Event ID, Journey ID and version, safe attempt timestamps and\ndispositions, key ID, signature scheme, correlation and replay lineage, plus a\nsafe explanation and next action. It never includes the signing secret or a\nresponse body.\n\nDelivery is at least once and may be out of order. Deduplicate business effects\nby Event ID, use the Journey version to recognize stale arrivals, and call\n`burjx.journeys.get(journeyId)` when current state matters. Retryable failures\nretain the same Delivery ID and use the base retry schedule of 1 minute,\n5 minutes, 30 minutes, 2 hours, 8 hours, and 24 hours. A valid receiver\n`Retry-After` can extend an interval by at most 24 hours; it never shortens the\nbase schedule. A manual replay is idempotent under its `idempotencyKey`: it\ncreates one new Delivery ID with immutable lineage to the original delivery but\ndoes not create a new regulated Event.\n\nEvery Event envelope declares `schemaMajor: \"v1\"` and contains a normalized\nsafe Journey snapshot. Additive optional fields remain compatible within v1.\nA breaking shape requires a new schema major and explicit subscription opt-in;\nBurjX does not silently change an existing subscription's major version.\n\n## Integration Readiness\n\n`await burjx.readiness.get()` returns the same Application-bound, read-only\nIntegration Readiness contract shown in the dashboard. It never starts an\nActivation Journey and there is no manual ready override.\n\nThe aggregate `state` is one of `setup_required`, `verification_required`,\n`ready`, `degraded`, or `suspended`. When several conditions apply, precedence\nis `suspended`, then `setup_required`, `verification_required`, `degraded`, and\nfinally `ready`. The complete `findings` array remains available even when a\nhigher-precedence finding determines the aggregate.\n\nEach typed finding includes its stable code, affected check and resource,\nseverity, safe next action, evidence and evaluation times, Application\nconfiguration version, and safe correlation metadata. Evidence changes only\nwhen a dependency changes: inactivity alone does not expire an otherwise valid\nobservation. Readiness accepts suitable current credential coverage, ignores\nretired credentials when another suitable credential remains, and evaluates\n`journey.created` coverage across the current verified Webhook Subscriptions.\n\nThe SDK sends an opaque generated request ID, its package version, and the\n`node` runtime family on authenticated requests so BurjX can expose a minimal\nsuccessful-use observation. The public observation adds current support status\nand last successful use. It excludes hostnames, source paths, secrets, payloads,\nand dependency inventory. This is evidence that the authenticated Application\nmade a successful request using the supported SDK wire contract; it is not a\ncryptographic attestation of a particular installed package binary.\n\n## Verify webhook signatures\n\nCapture the request body as its original bytes before parsing it. Select the\nsecret by the non-secret `burjx-key-id` header and verify all signing headers\nagainst those exact bytes; never parse and re-serialize JSON before\nverification.\n\n```ts\nimport { verifyWebhookRequest } from \"@burjx/partner-sdk/webhooks\";\n\nexport const receiveBurjXWebhook = async (request: Request) => {\n  const rawBody = new Uint8Array(await request.arrayBuffer());\n  const headers = Object.fromEntries(request.headers);\n  const valid = await verifyWebhookRequest({\n    headers,\n    rawBody,\n    resolveSecret: async (keyId) =>\n      loadWebhookSecretFromYourSecretManager(keyId),\n  });\n  if (!valid) {\n    return new Response(\"Invalid or stale BurjX webhook\", {\n      status: 401,\n    });\n  }\n\n  // Signature and freshness are proven; parsing is now safe.\n  const event: unknown = JSON.parse(new TextDecoder().decode(rawBody));\n  console.info(\"Verified BurjX Event\", event);\n  return new Response(null, { status: 204 });\n};\n```\n\nThe verifier resolves only the named key, then computes HMAC-SHA256 over\n`timestamp + \".\" + eventId + \".\" + rawBody`, compares the digest in constant\ntime, requires a 10-digit Unix-seconds timestamp, validates header formats, and\nrejects timestamps more than five minutes old or ahead by default. Set\n`toleranceSeconds` only when your documented ingress policy requires a\ndifferent bounded window. Parse and validate the Event only after this check.\nDuring an explicit rotation cutover, retain the old key only for BurjX's\nbounded in-flight retry horizon. Remove it after that horizon; an audited\nemergency revocation ends its validity immediately.\n","readmeFilename":"README.md","_rev":"1-4da19d753c427f2659e0ecb10249792d"}