SDK (TypeScript)
@collaterate/partner-api is a typed TypeScript client for this API: typed requests and
responses, async-iterator pagination over nextCursor, a typed ApiError, automatic
429/503 retry honoring Retry-After, and a one-line webhook-signature verifier. If you are
integrating from Node or TypeScript, prefer it over hand-rolling the calls the rest of this
site documents at the fetch/curl level.
It is distributed from a private registry, not the public npm registry, and reuses the same
Cognito verification the REST API uses - but for only one of the REST API’s credential kinds.
The Cognito client_credentials token you already mint for API calls (clientId/clientSecret,
the normal way a partner’s own service integrates) also authenticates npm install: there is no
second credential to request, rotate, or revoke, and revoking API access (the existing
credential-revocation path) revokes SDK access in the same act. An interactive staff sign-in
token (the one a partner’s own person gets from signing in through a browser, rather than a
service holding a clientId/clientSecret) does not authenticate the install and is
rejected with the same 401 an invalid credential gets - this only matters if you were planning
to install using a personal sign-in rather than a service credential, which is not the documented
path below.
1. One-time .npmrc setup
Section titled “1. One-time .npmrc setup”Add these two lines to your project’s .npmrc:
@collaterate:registry=https://sdk.collaterate.com///sdk.collaterate.com/:_authToken=${COLLATERATE_API_TOKEN}Every URL in this guide is the production one. For the sandbox, substitute collaterail.com for
collaterate.com throughout - see Environments.
${COLLATERATE_API_TOKEN} is npm’s own syntax for reading an environment variable at install
time - .npmrc never holds the token itself, only this reference.
2. Install
Section titled “2. Install”With .npmrc in place and COLLATERATE_API_TOKEN exported:
npm install @collaterate/partner-api3. What an expired or wrong token looks like
Section titled “3. What an expired or wrong token looks like”An expired token, a token from the wrong environment’s user pool, or a partner credential that
has been disabled or revoked all fail the same way an npm install failure always looks from
npm’s own CLI - not something this SDK controls, since the registry broker’s 401 is what npm
itself surfaces. The distinguishing question is the same one
Authentication already answers for
the REST API: a 401 here never explains which check failed, on purpose, so the fix is
always the same regardless of cause - mint a fresh COLLATERATE_API_TOKEN and retry before
assuming anything else is wrong.
Captured from a real revoked credential:
npm error code E401npm error 401 Unauthorized - GET https://sdk.collaterate.com/@collaterate%2fpartner-api - unauthorizedTwo details worth knowing when you read that line. The URL npm reports has the scope’s /
percent-encoded as %2f, which is npm’s own doing and not a sign of a malformed .npmrc. And
the request that fails is the packument fetch - the first call npm makes - so a 401 here
means you never got as far as downloading a tarball, and nothing was partially installed.
4. Versioning and upgrading
Section titled “4. Versioning and upgrading”The SDK is versioned independently of the API’s own v1/v2 scheme - a fix or an ergonomic
addition ships as an SDK minor or patch with no API change involved, and the SDK’s version
number has no required relationship to which API version it happens to target today. Only
v1 exists, so this is not yet a live distinction, but it means an SDK upgrade is never itself
evidence that the underlying API contract changed.
Upgrade the same way you would any other npm dependency:
npm install @collaterate/partner-api@latestEvery published version stays installable - publishing a new version merges it into the registry’s version list rather than replacing it, so pinning an older version in your lockfile continues to resolve. Read Versioning for what counts as an additive, in-place change versus one that ships as a new version, which governs the API underneath the SDK exactly as it does the raw REST contract.
5. A worked example
Section titled “5. A worked example”Install, construct a client, list products, submit an order, and verify a webhook - the SDK equivalent of the raw calls in Getting started and Submitting orders.
npm install @collaterate/partner-apiimport { ApiError, PartnerApiClient, verifyWebhookSignature, type WebhookEvent,} from '@collaterate/partner-api';
const client = new PartnerApiClient({ clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, baseUrl: 'https://api.partners.collaterate.com',});
async function listProducts(): Promise<void> { // Async-iterates every page, walking `nextCursor` automatically -- no cursor loop to write. for await (const product of client.products.list({ siteId: 7 })) { console.log(product.productId, product.name); }}
async function submitOrder(): Promise<void> { try { // `partnerOrderId` is the idempotency key: resubmitting the identical body under an // already-used id returns the original submission rather than creating a second order. const submission = await client.orders.create({ siteId: 7, partnerOrderId: 'acme-po-4471', shipTo: { name: 'Jane Doe', address1: '123 Main St', city: 'Springfield', state: 'IL', postalCode: '62701', country: 'US', }, lines: [{ productId: 'SLO_918273', partnerLineId: 'acme-line-1', quantity: 500, variantId: 55012 }], });
// `POST /v1/orders` waits up to 20 seconds for a terminal state, so `submission.status` is // `completed`/`failed` on roughly 19 of 20 calls. If it is still `queued`/`processing`, // poll `client.submissions.get(submission.submissionId)` -- see Submitting orders. if (submission.status === 'completed') { console.log('order number', submission.order?.orderNumber); } else if (submission.status === 'failed') { console.error('submission rejected', submission.errors); } } catch (error) { if (error instanceof ApiError) { // error.code is the field to branch on -- see the Errors guide's code catalog. Retryable // 429/503 responses never reach this catch: the client already retried them internally. console.error(`${error.status} ${error.code}: ${error.detail}`); } else { throw error; } }}
// In your webhook endpoint handler, before you parse or act on the body:function handleWebhook(rawBody: string, signatureHeader: string): void { const valid = verifyWebhookSignature(process.env.WEBHOOK_SECRET!, rawBody, signatureHeader); if (!valid) { throw new Error('invalid webhook signature'); }
// Only parse a body whose signature checked out. `WebhookEvent` types the result, so // `eventId`/`type`/`orderNumber` are checked rather than hand-declared. const event = JSON.parse(rawBody) as WebhookEvent;
// The event names what changed and carries no order data of its own -- re-read the // resource for what is actually true. Dedupe on `event.eventId` first: best-effort // delivery does not mean at-most-once, and the same event can arrive twice. if (event.type === 'order.status_changed') { void client.orders.get(event.orderNumber); }
// Do not fail on a `type` you do not recognize. `WebhookEvent['type']` is deliberately an // open union: an operator can add a type to your endpoint's subscription, effective // immediately, before your next deploy. Compare against `KnownWebhookEventType` if you want // the known set by name -- see the Webhooks guide.}Three things worth noting against the raw-fetch walkthroughs elsewhere on this site:
- No token handling of your own.
PartnerApiClientmints and caches the Cognito token internally and refetches on expiry or on a401; the token is never exposed on the client’s public interface. This is the API’s own OAuth2client_credentialscredential (clientId/clientSecret) - a different credential fromCOLLATERATE_API_TOKENabove, which exists only to authenticate the package install. - No manual retry loop.
429and503are retried automatically, honoringRetry-After. Passretry: falsetoPartnerApiClient’s constructor if you want manual control instead. ApiErrorcarriesstatus,code,detailandrequestId- the sameapplication/problem+jsonfields Errors documents, just parsed for you rather than something you parse yourself.
6. Flat filters and nested methods, side by side
Section titled “6. Flat filters and nested methods, side by side”The REST API publishes both a flat collection with a query filter and a nested path that reads
the same collection scoped to one parent - see
Users and divisions for the REST-level
detail. The SDK mirrors this rather than picking one: client.users.list({ siteId: 222 }) and
client.users.listBySite(222) are the same request, same result, and neither is deprecated in
favor of the other.
// Walking one site's tree - the nested form reads naturally in that context.for await (const user of client.users.listBySite(222)) { console.log(user.userId);}
// Reconciling everything you can see - page the flat, unfiltered form instead. With 419,979// users across 475 sites, one paged sweep beats one call per site.for await (const user of client.users.list()) { console.log(user.userId);}The same pair exists on client.divisions, client.credit, client.segments,
client.products and client.orders for their own GET /v1/sites/{siteId}/... paths, and
client.users.listByDivision(divisionId) mirrors GET /v1/divisions/{divisionId}/users. Each
nested method takes the parent id as its first argument and whatever further filters that nested
path itself accepts (see docs/openapi/v1.yaml for the exact set per path) - the flat method’s own
filter parameter is unchanged and still there.
client.sites.getSite(siteId) is new too: GET /v1/sites/{siteId} had no SDK method before this
release.
- Getting started - the raw-
fetchwalkthrough this SDK replaces, if you need to see a call at the wire level. - Submitting orders - the full submission lifecycle
client.orders.create()wraps, including the fast path and the poll. - Webhooks - everything
verifyWebhookSignaturechecks for you, and what to do with a delivery once it verifies. - Errors - the full
codecatalogApiError.codesurfaces.