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

attio-ts-sdk

v1.0.0

Published

Type-safe TypeScript SDK for the Attio CRM API with Zod v4 runtime validation

Downloads

204

Readme

Attio CRM TypeScript SDK

npm version ci codecov NPM License Context7 Ask DeepWiki

A modern, type-safe TypeScript SDK for the Attio CRM API. Built with Zod v4 and a new Attio‑aware client layer that adds retries, error normalization, caching, and higher‑level helpers on top of the generated OpenAPI client.

  • Create an Attio client in one line (createAttioClient({ apiKey }))
  • Retry & rate‑limit aware (exponential backoff + Retry-After)
  • Normalized errors (consistent shape + optional suggestions for select/status mismatches)
  • Record normalization (handles inconsistent response shapes)
  • Metadata caching (attributes, select options, statuses)
  • Pagination helpers (paginate + cursor handling)

You still have full access to the generated, spec‑accurate endpoints.

Features

  • Full Attio API Coverage - People, companies, lists, notes, tasks, meetings, webhooks, and more
  • Runtime Validation - Every request and response validated with Zod v4 schemas
  • Tree-Shakeable - Import only what you need
  • TypeScript First - Complete type definitions generated from OpenAPI spec
  • Attio-Aware Client - Retries, normalized errors, caching, helpers
  • Zero Config - Sensible defaults, just add your API key

Installing

# pnpm (recommended)
pnpm add attio-ts-sdk zod

# npm
npm install attio-ts-sdk zod

# yarn
yarn add attio-ts-sdk zod

# bun
bun add attio-ts-sdk zod

Note: Zod v4 is a peer dependency - install it alongside the SDK.

Usage

This SDK provides two layers:

  1. Attio helpers (recommended): createAttioClient, createRecord, queryRecords, etc.
  2. Generated endpoints: getV2Objects, postV2ObjectsByObjectRecordsQuery, etc.

Quick Start

import { createAttioClient, getV2Objects, postV2ObjectsByObjectRecordsQuery } from 'attio-ts-sdk';

// Configure the client with your API key
const client = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
});

// List all objects in your workspace
const { data: objects } = await getV2Objects({ client });
console.log(objects);

// Query people records
const { data: people } = await postV2ObjectsByObjectRecordsQuery({
  client,
  path: { object: 'people' },
  body: {
    limit: 10,
    sorts: [{ attribute: 'created_at', direction: 'desc' }],
  },
});

Attio Convenience Layer

The Attio helpers wrap the generated endpoints with retries, error normalization, record normalization, and opinionated defaults.

import { createAttioClient, createRecord, listLists, searchRecords } from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });

const lists = await listLists({ client });

const company = await createRecord({
  client,
  object: 'companies',
  values: {
    name: [{ value: 'Acme Corp' }],
    domains: [{ domain: 'acme.com' }],
  },
});

const matches = await searchRecords({
  client,
  query: 'acme.com',
  objects: ['companies'],
});

Client Configuration

import { createAttioClient } from 'attio-ts-sdk';

const client = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  baseUrl: 'https://api.attio.com',
  timeoutMs: 20_000,
  retry: { maxRetries: 4 },
  cache: { enabled: true },
});

Error Handling

Errors are normalized to AttioError / AttioApiError / AttioNetworkError.

import { createAttioClient, createRecord, AttioError } from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });

try {
  await createRecord({
    client,
    object: 'companies',
    values: { stage: [{ value: 'Prospectt' }] },
  });
} catch (err) {
  const error = err as AttioError;
  console.log(error.status, error.code, error.requestId, error.suggestions);
}

Pagination Helpers

import { createAttioClient, paginate, getV2Meetings } from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });


const meetings = await paginate(async (cursor) => {
  const result = await getV2Meetings({
    client,
    query: { cursor },
  });
  return result;
});

Caching

The SDK includes two levels of caching to reduce API calls and improve performance:

Metadata Caching

Attribute metadata (attributes, select options, and statuses) is automatically cached with a 5-minute TTL. This reduces redundant API calls when working with the same objects repeatedly.

import { getAttributeOptions, getAttributeStatuses, listAttributes } from 'attio-ts-sdk';

// These calls are cached for 5 minutes
const options = await getAttributeOptions({
  client,
  target: 'objects',
  identifier: 'companies',
  attribute: 'stage',
});

// Subsequent calls with the same parameters return cached data
const optionsAgain = await getAttributeOptions({
  client,
  target: 'objects',
  identifier: 'companies',
  attribute: 'stage',
}); // Returns cached result, no API call

The metadata caches have the following limits:

  • Attributes cache: 200 entries max
  • Options cache: 500 entries max
  • Statuses cache: 500 entries max

When a cache reaches its limit, the oldest entry is evicted.

Client Instance Caching

You can cache AttioClient instances to reuse them across your application. This is useful when you want to avoid creating new client instances for repeated operations.

import { getAttioClient } from 'attio-ts-sdk';

// With cache.key set, the client instance is cached and reused
const client = getAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  cache: { key: 'my-app' },
});

// Returns the same cached client instance
const sameClient = getAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  cache: { key: 'my-app' },
});

// Disable caching if needed
const freshClient = getAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  cache: { enabled: false },
});

Note: createAttioClient always creates a new client instance. Use getAttioClient when you want caching behavior.

Metadata Helpers

import { createAttioClient, getAttributeOptions } from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });

const options = await getAttributeOptions({
  client,
  target: 'objects',
  identifier: 'companies',
  attribute: 'stage',
});

Working with Records

import {
  createAttioClient,
  createRecord,
  upsertRecord,
  getRecord,
  deleteRecord,
} from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });

// Create a new company
const newCompany = await createRecord({
  client,
  object: 'companies',
  values: {
    name: [{ value: 'Acme Corp' }],
    domains: [{ domain: 'acme.com' }],
  },
});

// Upsert a record (create or update based on matching attribute)
const upserted = await upsertRecord({
  client,
  object: 'companies',
  matchingAttribute: 'domains',
  values: {
    name: [{ value: 'Acme Corp' }],
    domains: [{ domain: 'acme.com' }],
    description: [{ value: 'Updated description' }],
  },
});

// Get a specific record
const company = await getRecord({
  client,
  object: 'companies',
  recordId: 'abc-123',
});

// Delete a record
await deleteRecord({
  client,
  object: 'companies',
  recordId: 'abc-123',
});

Using Generated Endpoints Directly

You can always call the generated endpoints for full spec coverage:

import { createAttioClient, getV2Objects } from 'attio-ts-sdk';

const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });
const { data: objects } = await getV2Objects({ client });

Managing Lists

import {
  getV2Lists,
  postV2ListsByListEntriesQuery,
  postV2ListsByListEntries,
} from 'attio-ts-sdk';

// Get all lists
const { data: lists } = await getV2Lists({ client });

// Query entries in a list
const { data: entries } = await postV2ListsByListEntriesQuery({
  client,
  path: { list: 'sales-pipeline' },
  body: {
    filter: {
      attribute: 'stage',
      value: 'negotiation',
    },
  },
});

// Add a record to a list
const { data: entry } = await postV2ListsByListEntries({
  client,
  path: { list: 'sales-pipeline' },
  body: {
    data: {
      parent_record_id: 'company-record-id',
      entry_values: {
        stage: [{ status: 'prospecting' }],
        deal_value: [{ currency_value: 50000 }],
      },
    },
  },
});

Notes and Tasks

import { postV2Notes, postV2Tasks, patchV2TasksByTaskId } from 'attio-ts-sdk';

// Create a note on a record
const { data: note } = await postV2Notes({
  client,
  body: {
    data: {
      parent_object: 'companies',
      parent_record_id: 'abc-123',
      title: 'Meeting Notes',
      content: 'Discussed Q4 roadmap...',
    },
  },
});

// Create a task
const { data: task } = await postV2Tasks({
  client,
  body: {
    data: {
      content: 'Follow up on proposal',
      deadline_at: '2024-12-31T17:00:00Z',
      linked_records: [{ target_object: 'companies', target_record_id: 'abc-123' }],
    },
  },
});

// Mark task as complete
await patchV2TasksByTaskId({
  client,
  path: { task_id: task.data.id.task_id },
  body: { data: { is_completed: true } },
});

Webhooks

import { postV2Webhooks, getV2Webhooks } from 'attio-ts-sdk';

// Create a webhook
const { data: webhook } = await postV2Webhooks({
  client,
  body: {
    data: {
      target_url: 'https://your-app.com/webhooks/attio',
      subscriptions: [
        { event_type: 'record.created', filter: { object: 'companies' } },
        { event_type: 'record.updated', filter: { object: 'companies' } },
      ],
    },
  },
});

// List all webhooks
const { data: webhooks } = await getV2Webhooks({ client });

Error Handling

import { postV2ObjectsByObjectRecords } from 'attio-ts-sdk';

const result = await postV2ObjectsByObjectRecords({
  client,
  path: { object: 'companies' },
  body: { data: { values: { name: [{ value: 'Test' }] } } },
});

if (result.error) {
  console.error('API Error:', result.error);
} else {
  console.log('Created:', result.data);
}

// Or use throwOnError for exceptions
try {
  const { data } = await postV2ObjectsByObjectRecords({
    client,
    path: { object: 'companies' },
    body: { data: { values: { name: [{ value: 'Test' }] } } },
    throwOnError: true,
  });
} catch (error) {
  console.error('Request failed:', error);
}

Development

Tools

  • Hey API: OpenAPI client and Zod schema generation
  • Biome: lint and format with a single tool
  • Vitest: fast tests with coverage and thresholds
  • tsdown: ESM builds for Node
  • CI: lint, typecheck, test, coverage, and size comments/badges
  • Deno-friendly: .ts source imports for direct consumption
  • OIDC + Provenance: publish to npm and JSR via manual CI release

Setup

Install dependencies and run scripts:

git clone [email protected]:hbmartin/attio-ts-sdk.git
cd attio-ts-sdk
pnpm i
pnpm lint
pnpm test
pnpm build

Releasing

  • Merge the automated Release PR created by Release Please
  • Manually run the "Release" workflow to publish to npm and JSR with provenance

License

MIT © Harold Martin