npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@itm-studio/partner-sdk

v0.2.1

Published

TypeScript SDK for the ITM Partner API — typed GraphQL client with zero introspection required

Readme

@itm-studio/partner-sdk

Type-safe TypeScript SDK for the ITM Partner API. Built with genql — full autocomplete, zero introspection required.

CI Schema Registry

A full implementation example can be found here: demo-events-sdk-integration

Install

npm install @itm-studio/partner-sdk

Quick Start

import { createITMPartnerClient } from '@itm-studio/partner-sdk';

const itm = createITMPartnerClient({
  token: process.env.ITM_PARTNER_TOKEN!,
});

// Get all upcoming moments for your brand
const { getPartnerMomentsForBrand } = await itm.query({
  getPartnerMomentsForBrand: {
    __args: { status: 'UPCOMING', take: 10 },
    moments: {
      uid: true,
      name: true,
      slug: true,
      startDate: true,
      endDate: true,
      status: true,
      timezone: true,
      externalUrl: true,
      coverImage: {
        url: true,
        mimeType: true,
      },
      venue: {
        name: true,
        city: true,
        country: true,
      },
      ticketTiers: {
        name: true,
        price: true,
        currency: { code: true, symbol: true },
        soldOut: true,
      },
    },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

Authentication

Get your partner API token from the ITM backstage dashboard: Settings > Partner API.

The token is sent as the x-partner-api-key header on every request. Your brand is determined automatically from the token — no need to pass brand IDs.

Available Queries

getPartnerMomentsForBrand

Get paginated moments (events) for your brand.

const { getPartnerMomentsForBrand } = await itm.query({
  getPartnerMomentsForBrand: {
    __args: {
      take: 20,             // items per page (default: 50, omit to use default)
      cursor: nextCursor,   // from previous response
      status: 'UPCOMING',   // UPCOMING | LIVE | ENDED
      sortOrder: 'ASC',     // ASC | DESC by start date (default: DESC)
    },
    moments: { uid: true, name: true, startDate: true },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

getPartnerMoment

Get a single moment by slug.

const { getPartnerMoment } = await itm.query({
  getPartnerMoment: {
    __args: { slug: 'my-event-slug' },
    uid: true,
    name: true,
    startDate: true,
    endDate: true,
    timezone: true,
    externalUrl: true,
    coverImage: {
      url: true,
      mimeType: true,
    },
    ticketTiers: {
      name: true,
      price: true,
      soldOut: true,
    },
  },
});

getPartnerTicketsForMoment

Get paginated tickets for a moment.

const { getPartnerTicketsForMoment } = await itm.query({
  getPartnerTicketsForMoment: {
    __args: {
      momentSlug: 'my-event-slug',
      checkedIn: true,  // optional: filter by check-in status
      take: 50,
    },
    tickets: {
      uid: true,
      price: true,
      redeemedAt: true,
      hasExpired: true,
      user: { uid: true, name: true, primaryEmail: true },
      ticketTier: { name: true, price: true },
      payment: { amount: true, status: true },
    },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

getPartnerCustomersForMoment

Get paginated customers (users with their tickets) for a moment.

const { getPartnerCustomersForMoment } = await itm.query({
  getPartnerCustomersForMoment: {
    __args: { momentSlug: 'my-event-slug', take: 50 },
    customers: {
      user: { uid: true, name: true, primaryEmail: true, phoneNumber: true },
      tickets: { uid: true, price: true, redeemedAt: true },
    },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

getPartnerMomentCollection

Get a moment collection by slug. Collections group related moments together (e.g. a summer series, a festival lineup). Returns the collection with its moments and links.

const { getPartnerMomentCollection } = await itm.query({
  getPartnerMomentCollection: {
    __args: { slug: 'summer-series' },
    uid: true,
    name: true,
    description: true,
    subLabel: true,
    availableQueryFilters: true,
    moments: {
      uid: true,
      name: true,
      slug: true,
      startDate: true,
      endDate: true,
      status: true,
      timezone: true,
      externalUrl: true,
      coverImage: {
        url: true,
        mimeType: true,
      },
      venue: { name: true, city: true },
      ticketTiers: {
        name: true,
        price: true,
        currency: { code: true, symbol: true },
        soldOut: true,
      },
    },
    links: {
      uid: true,
      url: true,
      label: true,
    },
  },
});

You can filter moments within a collection using queryFilters:

const { getPartnerMomentCollection } = await itm.query({
  getPartnerMomentCollection: {
    __args: { slug: 'summer-series' },
    name: true,
    moments: {
      __args: { queryFilters: ['upcoming'] },
      uid: true,
      name: true,
      startDate: true,
    },
  },
});

Note: getPartnerMomentCollection returns null if no collection matches the given slug.

getPartnerPublicEchoes

Get public echoes (content) for your brand. Echoes are content units — links, posts, images, videos, galleries, audio, polls, forms, and third-party embeds (Spotify, Instagram, TikTok).

  • If momentSlug is provided, returns moment-level echoes for that moment.
  • If momentSlug is omitted, returns brand-level echoes.
const { getPartnerPublicEchoes } = await itm.query({
  getPartnerPublicEchoes: {
    __args: { take: 20 },
    echoes: {
      uid: true,
      type: true,
      name: true,
      slug: true,
      description: true,
      publishTime: true,
      externalLink: true,
      mediaAsset: {
        uid: true,
        mimeType: true,
        s3Key: true,
        dimensions: { width: true, height: true },
        placeholderUrl: true,
        blurhash: true,
      },
      echoMedia: {
        uid: true,
        mimeType: true,
        s3Key: true,
        dimensions: { width: true, height: true },
      },
      moment: { uid: true, name: true, slug: true },
    },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

Filter by moment to get echoes scoped to a specific event:

const { getPartnerPublicEchoes } = await itm.query({
  getPartnerPublicEchoes: {
    __args: { momentSlug: 'my-event-slug', take: 10 },
    echoes: {
      uid: true,
      type: true,
      name: true,
      postBody: true,
      externalLink: true,
      thirdPartyEchoType: true,
      thirdPartyEchoConfig: true,
    },
    totalCount: true,
    hasNextPage: true,
    nextCursor: true,
  },
});

Echo types: LINK | POST | IMAGE | VIDEO | GALLERY | MERCH | AUDIO | QUESTION | POLL | UPLOAD | TOKEN | MOMENT | FORM | THIRD_PARTY

Third-party embed types: SPOTIFY | INSTAGRAM | TIKTOK — use thirdPartyEchoType and thirdPartyEchoConfig for embed details.

Available Mutations

The SDK supports write operations via client.mutation(). All mutations use the same authentication — your brand is determined from the token.

Media Upload Flow

Uploading media (e.g. cover images) is a two-step process:

Step 1: getPartnerUploadUrl

Get a presigned S3 URL to upload a file directly.

const { getPartnerUploadUrl } = await itm.mutation({
  getPartnerUploadUrl: {
    __args: { filename: 'event-cover', fileExtension: 'jpg' },
    url: true,
    filename: true,
  },
});

// Upload the file to S3 using the presigned URL
await fetch(getPartnerUploadUrl.url, {
  method: 'PUT',
  body: fileBuffer, // Buffer, Blob, or ReadableStream
  headers: { 'Content-Type': 'image/jpeg' },
});

Step 2: createPartnerMediaAsset

After uploading, register the asset so it's linked to your brand.

const { createPartnerMediaAsset } = await itm.mutation({
  createPartnerMediaAsset: {
    __args: {
      mimetype: 'image/jpeg',
      filename: getPartnerUploadUrl.filename, // from Step 1
      dimensions: { width: 1920, height: 1080 },
    },
    uid: true,
    url: true,
    mimeType: true,
  },
});

// Use createPartnerMediaAsset.uid as coverImageUid when creating a moment

createPartnerMoment

Create a moment (event) with optional venue and ticket tiers in one atomic operation.

const { createPartnerMoment } = await itm.mutation({
  createPartnerMoment: {
    __args: {
      input: {
        name: 'Summer Rooftop Party',
        description: 'A rooftop party in NYC',
        blurb: 'Join us on the roof!',
        startDate: '2026-07-15T20:00:00.000Z',
        endDate: '2026-07-16T02:00:00.000Z',
        timezone: 'America/New_York',
        type: 'IRL',
        coverImageUid: createPartnerMediaAsset.uid, // from upload flow
        venue: {
          name: 'Rooftop Bar',
          city: 'New York',
          country: 'US',
          address: '123 Main St',
        },
        category: 'PARTY',
        subcategory: 'ROOFTOP_PARTY',
        ticketTiers: [
          {
            name: 'General Admission',
            price: 2500,          // $25.00 in cents
            supply: 200,
            maxPerUser: 4,
            currencyCode: 'USD',
          },
          {
            name: 'VIP',
            price: 7500,          // $75.00 in cents
            supply: 50,
            maxPerUser: 2,
            currencyCode: 'USD',
            description: 'Includes open bar',
          },
        ],
      },
    },
    uid: true,
    name: true,
    slug: true,
    startDate: true,
    endDate: true,
    status: true,
    coverImage: { url: true },
    venue: { name: true, city: true },
    ticketTiers: {
      uid: true,
      name: true,
      price: true,
      currency: { code: true, symbol: true },
      soldOut: true,
    },
  },
});

Required fields: name, description, blurb, startDate, endDate, timezone, type

Moment types:

  • IRL — in-person event (requires venue)
  • DIGITAL — online event (no venue needed)

Ticket tier pricing: price is in cents (e.g. 2500 = $25.00). Use 0 for free tiers.

Complete Upload + Create Moment Flow

import { createITMPartnerClient } from '@itm-studio/partner-sdk';
import { readFile } from 'fs/promises';

const itm = createITMPartnerClient({
  token: process.env.ITM_PARTNER_TOKEN!,
});

// 1. Get presigned upload URL
const { getPartnerUploadUrl } = await itm.mutation({
  getPartnerUploadUrl: {
    __args: { filename: 'event-cover', fileExtension: 'jpg' },
    url: true,
    filename: true,
  },
});

// 2. Upload file to S3
const file = await readFile('./event-cover.jpg');
await fetch(getPartnerUploadUrl.url, {
  method: 'PUT',
  body: file,
  headers: { 'Content-Type': 'image/jpeg' },
});

// 3. Register the uploaded asset
const { createPartnerMediaAsset } = await itm.mutation({
  createPartnerMediaAsset: {
    __args: {
      mimetype: 'image/jpeg',
      filename: getPartnerUploadUrl.filename,
      dimensions: { width: 1920, height: 1080 },
    },
    uid: true,
  },
});

// 4. Create the moment with the uploaded cover image
const { createPartnerMoment } = await itm.mutation({
  createPartnerMoment: {
    __args: {
      input: {
        name: 'My Event',
        description: 'An amazing event',
        blurb: 'Come join us!',
        startDate: '2026-07-15T20:00:00.000Z',
        endDate: '2026-07-16T02:00:00.000Z',
        timezone: 'America/New_York',
        type: 'IRL',
        coverImageUid: createPartnerMediaAsset.uid,
        venue: {
          name: 'The Venue',
          city: 'New York',
          country: 'US',
        },
        ticketTiers: [
          {
            name: 'General Admission',
            price: 2500,
            supply: 100,
            maxPerUser: 4,
            currencyCode: 'USD',
          },
        ],
      },
    },
    uid: true,
    slug: true,
    status: true,
    ticketTiers: { uid: true, name: true },
  },
});

console.log(`Created: ${createPartnerMoment.slug} (${createPartnerMoment.status})`);

Pagination

All list endpoints use cursor-based pagination. The default page size is 50 items if take is not specified.

let cursor: string | null = null;

do {
  const { getPartnerTicketsForMoment } = await itm.query({
    getPartnerTicketsForMoment: {
      __args: { momentSlug: 'my-event', cursor, take: 50 },
      tickets: { uid: true, user: { name: true } },
      hasNextPage: true,
      nextCursor: true,
      totalCount: true,
    },
  });

  for (const ticket of getPartnerTicketsForMoment.tickets) {
    console.log(ticket.user.name);
  }

  cursor = getPartnerTicketsForMoment.nextCursor ?? null;
} while (cursor);

Custom Base URL

For staging or local development:

const itm = createITMPartnerClient({
  token: process.env.ITM_PARTNER_TOKEN!,
  baseUrl: 'https://staging-api.itm.studio/graphql',
});

Development

npm install
npm run generate   # Generate typed client from schema
npm test           # Run tests
npm run build      # Build for publishing

Architecture

itm-backend (NestJS)                    itm-partner-sdk
┌─────────────────────┐                ┌────────────────────────┐
│ Full GraphQL Schema  │                │ schema/partner.graphql │
│ (8000+ lines)        │  ── export ──▶ │ (partner subset only)  │
│                      │                │                        │
│ BrandPartnerGuard    │                │        genql           │
│ x-partner-api-key    │                │          │             │
│ Authorization Bearer │                │          ▼             │
└─────────────────────┘                │ src/generated/ (typed) │
         ▲                              │          │             │
         │                              │          ▼             │
         │    x-partner-api-key         │ createITMPartnerClient │
         └──────────────────────────────│                        │
                                        └────────────────────────┘
                                                   │
                                                   ▼
                                        ┌────────────────────────┐
                                        │   GraphQL Hive          │
                                        │   Schema Registry       │
                                        │   Breaking change guard │
                                        └────────────────────────┘
  • Partner schema is a curated subset of the full backend schema — only partner-safe endpoints
  • genql generates a fully typed client from the schema at build time — no runtime introspection
  • GraphQL Hive validates schema changes on every PR and blocks breaking changes
  • Authentication uses the existing BrandPartnerGuard — token sent via x-partner-api-key header

Schema Governance (Hive)

This SDK uses GraphQL Hive for schema governance.

# Local usage (requires HIVE_TOKEN env var)
npm run schema:check    # Check for breaking changes
npm run schema:publish  # Publish schema to registry

CI/CD

| Job | Trigger | What it does | |---|---|---| | build-and-test | Every push & PR | Install, generate, lint, test, build | | schema-check | PRs only | Validates schema changes won't break partners | | schema-publish | Push to main | Publishes schema to Hive registry | | publish | Push to main | Auto-bumps patch version and publishes to NPM |

Adding New Partner Endpoints

When exposing a new backend endpoint to partners:

  1. Add the query/mutation to schema/partner.graphql with only the fields partners should access
  2. Run npm run generate to regenerate the typed client
  3. Add tests in tests/queries.test.ts
  4. Open a PR — Hive will validate the schema change isn't breaking
  5. After merge, the schema is auto-published to Hive and a new SDK version can be released