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

v2.1.0

Published

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

Downloads

1,291

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 client layer that adds retries, error normalization, caching, and higher‑level helpers on top of the generated OpenAPI client.

  • Full Attio API Coverage - People, companies, lists, notes, tasks, meetings, webhooks, and more
  • Create a 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 + paginateOffset + cursor handling
  • 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

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

See Also

  • attio-js - an alternative SDK generated with Speakeasy
  • attio-tui - a TUI for using Attio built with the library

Table of Contents

Migrating to v2

Version 2.0 brings enhanced type safety, auto-pagination, and new filtering capabilities. Most code will work without changes, but there are a few breaking changes to be aware of.

Breaking Changes

ListId Validation

ListId values can no longer be empty strings. Use the new createListId() factory function:

// Before (v1)
const listId = 'sales-pipeline' as ListId;

// After (v2)
import { createListId } from 'attio-ts-sdk';
const listId = createListId('sales-pipeline');

The factory validates the input and throws if the string is empty.

Strongly Typed Filters

Filter types are now strongly typed instead of Record<string, unknown>. If you were passing arbitrary objects as filters, you may need to adjust your code to match the AttioFilter type.

New Features

Auto-Pagination

queryRecords and queryListEntries now support built-in pagination:

// Collect all pages automatically
const allRecords = await queryRecords({
  client,
  object: 'companies',
  paginate: true,
});

// Stream records with async generators (memory-efficient)
for await (const record of queryRecords({
  client,
  object: 'companies',
  paginate: 'stream',
})) {
  console.log(record.id);
}

Type-Safe Response Validation with itemSchema

All record and list entry functions now support itemSchema for Zod validation with full type inference:

import { z } from 'zod';

const companySchema = z.object({
  id: z.object({ record_id: z.string() }),
  values: z.object({
    name: z.array(z.object({ value: z.string() })),
  }),
});

type Company = z.infer<typeof companySchema>;

// TypeScript infers the return type from itemSchema
const companies = await queryRecords<Company>({
  client,
  object: 'companies',
  itemSchema: companySchema,
  paginate: true,
});
// companies is Company[] with full type safety

New Filter Operators

New comparison and path-based filter operators:

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

// Comparison operators
filters.lt('revenue', 100000)      // Less than
filters.lte('revenue', 100000)     // Less than or equal
filters.gt('revenue', 50000)       // Greater than
filters.gte('revenue', 50000)      // Greater than or equal
filters.in('status', ['active', 'pending'])  // Set membership
filters.between('revenue', 50000, 100000)    // Range (inclusive start, exclusive end)

// Path-based filters for record reference traversal
filters.path(
  [['companies', 'primary_contact']],
  { email: { $contains: '@acme.com' } }
)

AbortSignal Support

Pagination and query functions now accept signal for request cancellation:

const controller = new AbortController();

const records = await queryRecords({
  client,
  object: 'companies',
  paginate: true,
  signal: controller.signal,
});

// Cancel in-flight requests
controller.abort();

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.

Getting Your API Key

  1. Log in to your Attio workspace.
  2. Navigate to Workspace Settings → Developers (or visit https://app.attio.com/settings/developers directly).
  3. Click Create a new integration, give it a name, and select the scopes your application needs.
  4. Copy the generated API token and store it securely (e.g. in an environment variable).
export ATTIO_API_KEY="your-api-key-here"

The SDK reads the key from whatever you pass to createAttioClient({ apiKey }) — it does not read environment variables automatically, so you control exactly how the secret is loaded.

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' }],
  },
});

Recommended Pattern

Prefer the Attio convenience layer, throw on errors by default, and unwrap responses with helpers. This keeps request code compact and consistent.

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

const client = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  responseStyle: 'data',
  throwOnError: true,
});

const sdk = createAttioSdk({ client });

const company = await sdk.records.create({
  object: 'companies',
  values: {
    name: value.string('Acme Corp'),
    domains: value.domain('acme.com'),
    annual_revenue: value.currency(50000, 'USD'),
  },
});

// Use assertOk with generated endpoints when you need raw access
const objects = assertOk(await getV2Objects({ client }));
console.log(objects);

Attio SDK

createAttioSdk builds on top of the convenience layer and the generated endpoints to provide a single, namespaced object you can pass around your application. It binds the client once so you don't repeat { client } on every call, and groups operations by resource.

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

const sdk = createAttioSdk({ apiKey: process.env.ATTIO_API_KEY });

The returned sdk object exposes these namespaces:

| Namespace | Methods | | --- | --- | | sdk.objects | list, get, create, update | | sdk.records | create, update, upsert, get, delete, query | | sdk.lists | list, get, queryEntries, addEntry, updateEntry, removeEntry | | sdk.metadata | listAttributes, getAttribute, getAttributeOptions, getAttributeStatuses, schema |

The underlying AttioClient is also available as sdk.client when you need to drop down to the generated endpoints.

const companies = await sdk.records.query({
  object: 'companies',
  filter: { attribute: 'name', value: 'Acme' },
});

const attributes = await sdk.metadata.listAttributes({
  target: 'objects',
  identifier: 'companies',
});

// Use the generated endpoints when you need full spec access
const { data } = await getV2Objects({ client: sdk.client });

Attio Convenience Layer

The standalone helper functions wrap the generated endpoints with retries, error normalization, record normalization, and opinionated defaults. They are the same functions that createAttioSdk uses under the hood — use them directly when you prefer explicit { client } threading.

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'],
});

Value Helpers

The value namespace provides factory functions that build correctly shaped field-value arrays for record creation and updates. Each helper validates its input with Zod before returning, so typos and bad data fail fast at the call site rather than in the API response.

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

| Helper | Signature | Description | | --- | --- | --- | | value.string | (value: string) => ValueInput[] | Non-empty string field. | | value.number | (value: number) => ValueInput[] | Finite numeric field. | | value.boolean | (value: boolean) => ValueInput[] | Boolean field. | | value.domain | (value: string) => ValueInput[] | Domain field (non-empty string). | | value.email | (value: string) => ValueInput[] | Email field (validated format). | | value.currency | (value: number, currencyCode?: string) => ValueInput[] | Currency field. currencyCode is an optional ISO 4217 code (e.g. "USD"). |

const values = {
  name: value.string('Acme Corp'),
  domains: value.domain('acme.com'),
  contact_email: value.email('[email protected]'),
  employee_count: value.number(150),
  is_customer: value.boolean(true),
  annual_revenue: value.currency(50000, 'USD'),
};

await sdk.records.create({ object: 'companies', values });

Record Value Accessors

getValue and getFirstValue extract attribute values from a record object. Pass an optional Zod schema to get typed, validated results.

import { getFirstValue, getValue } from 'attio-ts-sdk';

// Untyped — returns unknown
const name = getFirstValue(company, 'name');
const domains = getValue(company, 'domains');

// Typed — returns parsed values or throws on mismatch
import { z } from 'zod';

const nameSchema = z.object({ value: z.string() });
const typedName = getFirstValue(company, 'name', { schema: nameSchema });
//    ^? { value: string } | undefined

Schema Helpers

Create a schema from cached metadata and use accessors to reduce raw string keys:

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

const schema = await createSchema({
  client,
  target: 'objects',
  identifier: 'companies',
});

const name = schema.getAccessorOrThrow('name').getFirstValue(company);

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

All errors thrown by the convenience layer and createAttioSdk are normalized into a hierarchy rooted at AttioError:

| Class | Default Code | When | | --- | --- | --- | | AttioApiError | (from response) | HTTP error responses (4xx / 5xx). Includes response, requestId, and optional retryAfterMs. | | AttioNetworkError | (from cause) | Connection failures, DNS errors, timeouts. | | AttioRetryError | RETRY_ERROR | All retry attempts exhausted. | | AttioResponseError | RESPONSE_ERROR | Response body failed Zod validation. | | AttioConfigError | CONFIG_ERROR | Invalid client configuration. | | AttioBatchError | BATCH_ERROR | A batch operation partially or fully failed. |

Every AttioError carries these optional fields:

error.status       // HTTP status code
error.code         // machine-readable error code
error.requestId    // Attio x-request-id header
error.retryAfterMs // parsed Retry-After (milliseconds)
error.suggestions  // fuzzy-match suggestions for value mismatches (see below)

Catching errors from the convenience layer

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) {
  if (err instanceof AttioError) {
    console.log(err.status, err.code, err.requestId, err.suggestions);
  } else {
    // Re-throw if it's not an error we specifically handle
    throw err;
  }
}

Smart suggestions for value mismatches

When an API error indicates a select option or status mismatch, the SDK automatically attaches a suggestions object with up to three fuzzy-matched alternatives:

error.suggestions
// {
//   field: 'stage',
//   attempted: 'Prospectt',
//   bestMatch: 'Prospect',
//   matches: ['Prospect', 'Prospecting', 'Closed']
// }

Response helpers for generated endpoints

When using the generated endpoints directly, use assertOk or toResult to unwrap responses:

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

// Throws on error, returns the data payload
const objects = assertOk(await getV2Objects({ client }));

// Returns a discriminated union { ok: true, value } | { ok: false, error }
const result = toResult(await getV2Objects({ client }));
if (result.ok) {
  console.log(result.value);
} else {
  console.error(result.error);
}

throwOnError mode

You can also opt into exceptions at the client level:

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

// Generated endpoints now throw instead of returning { error }
const { data } = await postV2ObjectsByObjectRecords({
  client,
  path: { object: 'companies' },
  body: { data: { values: { name: [{ value: 'Test' }] } } },
});

Pagination Helpers

The SDK provides multiple approaches to pagination, from simple convenience options to low-level helpers for full control.

Using queryRecords with auto-pagination (recommended)

The simplest way to paginate record queries is using the paginate option on queryRecords:

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

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

// Collect all pages automatically into an array
const allCompanies = await queryRecords({
  client,
  object: 'companies',
  filter: { attribute: 'name', value: 'Acme' },
  sorts: [{ attribute: 'created_at', direction: 'desc' }],
  paginate: true,
  maxItems: 10000,  // Optional: limit total items
});

// Stream records one at a time (memory-efficient for large datasets)
for await (const company of queryRecords({
  client,
  object: 'companies',
  paginate: 'stream',
})) {
  console.log(company.id);
}

The same pattern works with queryListEntries / sdk.lists.queryEntries.

Using low-level pagination helpers

For more control or when working directly with generated endpoints, use paginateOffset (offset-based) or paginate (cursor-based):

| Strategy | Helper | Endpoints | | --- | --- | --- | | Offset-based | paginateOffset | Record queries (postV2ObjectsByObjectRecordsQuery), list entry queries (postV2ListsByListEntriesQuery) | | Cursor-based | paginate | Meetings (getV2Meetings), notes (getV2Notes), tasks (getV2Tasks), webhooks, and most GET list endpoints |

Both helpers automatically extract items and pagination metadata from raw API responses.

Paginating record queries with paginateOffset

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

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

// Collect all companies matching a filter across every page
const allCompanies = await paginateOffset(async (offset, limit) => {
  return postV2ObjectsByObjectRecordsQuery({
    client,
    path: { object: 'companies' },
    body: {
      offset,
      limit,
      filter: { attribute: 'name', value: 'Acme' },
      sorts: [{ attribute: 'created_at', direction: 'desc' }],
    },
  });
});

Paginating list entry queries with paginateOffset

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

const allEntries = await paginateOffset(async (offset, limit) => {
  return postV2ListsByListEntriesQuery({
    client,
    path: { list: 'sales-pipeline' },
    body: {
      offset,
      limit,
      filter: { attribute: 'stage', value: 'negotiation' },
    },
  });
});

Type-safe response validation with itemSchema

The convenience functions queryListEntries and queryRecords support an optional itemSchema parameter for type-safe validation of API responses. The schema validates raw items before normalization.

import { z } from 'zod';
import { queryListEntries, createListId } from 'attio-ts-sdk';

// Define a schema that matches your expected item structure
const entrySchema = z.object({
  id: z.object({ entry_id: z.string() }),
  values: z.object({
    stage: z.array(z.object({ status: z.string() })),
    deal_value: z.array(z.object({ currency_value: z.number() })).optional(),
  }),
});

type SalesEntry = z.infer<typeof entrySchema>;

// Create a typed ListId using the factory function
const salesListId = createListId('sales-pipeline');

// TypeScript infers the return type from itemSchema
const entries = await queryListEntries<SalesEntry>({
  client,
  list: salesListId,
  itemSchema: entrySchema,
  paginate: true,
});

// entries is SalesEntry[] with full type safety
for (const entry of entries) {
  console.log(entry.values.stage[0].status);
}

When using streaming pagination, the same type safety applies:

const stream = queryListEntries<SalesEntry>({
  client,
  list: salesListId,
  itemSchema: entrySchema,
  paginate: 'stream',
});

for await (const entry of stream) {
  console.log(entry.values.stage[0].status);
}

Paginating cursor-based endpoints

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

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

Pagination options

Both helpers accept an options object to control limits:

// Offset-based options
const records = await paginateOffset(fetchPage, {
  offset: 0,         // starting offset (default: 0)
  limit: 100,        // items per page (default: 50)
  maxPages: 5,       // stop after N pages
  maxItems: 200,     // stop after N total items
});

// Cursor-based options
const meetings = await paginate(fetchPage, {
  cursor: null,      // starting cursor (default: null)
  maxPages: 10,      // stop after N pages
  maxItems: 500,     // stop after N total items
});

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 defaults:

  • 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.

You can customize TTL, max entries, and adapters per client:

const client = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  cache: {
    enabled: true,
    metadata: {
      ttlMs: 2 * 60 * 1000,
      maxEntries: { attributes: 300, options: 800, statuses: 800 },
      adapter: {
        create: ({ scope, ttlMs, maxEntries }) =>
          new YourCacheAdapter({ scope, ttlMs, maxEntries }),
      },
    },
  },
});

// Clear metadata caches for this client
client.cache.clear();

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 },
});

Debug Hooks

You can tap into request/response/error lifecycles for logging and tracing.

const client = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  hooks: {
    onRequest: ({ request }) => console.log("request", request.method, request.url),
    onResponse: ({ response }) => console.log("response", response.status),
    onError: ({ error }) => console.error("error", error.message),
  },
});

// Or wire a logger (debug/info/warn/error)
const clientWithLogger = createAttioClient({
  apiKey: process.env.ATTIO_API_KEY,
  logger: console,
});

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 });

See Also

  • attio-js - an alternative SDK generated with Speakeasy
  • attio-tui - a TUI for using Attio built with this library

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

License

MIT © Harold Martin