> attio-upgrade-migration

Migrate between Attio API versions, handle breaking changes in the v1-to-v2 transition, and plan for future deprecations. Trigger: "upgrade attio", "attio migration", "attio v1 to v2", "attio breaking changes", "attio API version", "attio deprecation".

fetch
$curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/attio-upgrade-migration?format=md"
SKILL.mdattio-upgrade-migration

Attio Upgrade & Migration

Overview

Attio has two API generations: v1 (legacy, deprecated) and v2 (current). This skill covers the v1-to-v2 migration, community SDK upgrade paths, and how to detect and adapt to API changes since Attio does not publish a traditional SDK changelog.

V1 to V2 Migration Reference

Endpoint Changes

OperationV1 EndpointV2 Endpoint
List objectsGET /v1/objectsGET /v2/objects
Query recordsGET /v1/objects/{id}/recordsPOST /v2/objects/{slug}/records/query
Create recordPOST /v1/objects/{id}/recordsPOST /v2/objects/{slug}/records
Get recordGET /v1/objects/{id}/records/{rid}GET /v2/objects/{slug}/records/{rid}
List entriesGET /v1/lists/{id}/entriesPOST /v2/lists/{slug}/entries/query
Create webhookPOST /v1/webhooksPOST /v2/webhooks
SearchN/APOST /v2/records/search

Key Differences

AspectV1V2
IdentifiersUUIDs onlySlugs (preferred) or UUIDs
Record queryGET with query paramsPOST with JSON body (filters, sorts)
FilteringBasic query paramsRich operators ($eq, $contains, $gt, $and, $or)
Paginationpage + per_pagelimit + offset or cursor-based
Webhook payloadsCustom formatConsistent with v2 response shapes
Webhook filteringNoneEvent-type and attribute-level filters

Step 1: Update Base URL

// Before
const BASE = "https://api.attio.com/v1";

// After
const BASE = "https://api.attio.com/v2";

Step 2: Migrate Record Queries

// V1: GET with query params
const v1 = await fetch(
  `${BASE}/objects/${objectId}/records?page=1&per_page=50`,
  { headers: { Authorization: `Bearer ${token}` } }
);

// V2: POST with filter body, using slug instead of UUID
const v2 = await fetch(
  `${BASE}/objects/people/records/query`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      filter: {
        email_addresses: { email_address: { $contains: "@example.com" } },
      },
      sorts: [{ attribute: "created_at", field: "created_at", direction: "desc" }],
      limit: 50,
      offset: 0,
    }),
  }
);

Step 3: Update Record Creation

// V1: values as flat key-value pairs
const v1Body = {
  name: "Ada Lovelace",
  email: "ada@example.com",
};

// V2: values nested under data.values, always arrays
const v2Body = {
  data: {
    values: {
      name: [{ first_name: "Ada", last_name: "Lovelace", full_name: "Ada Lovelace" }],
      email_addresses: ["ada@example.com"],
    },
  },
};

Step 4: Migrate Webhooks from V1 to V2

// V1 webhook event types
"object.record.created"

// V2 webhook event types
"record.created"
"record.updated"
"record.deleted"
"record.merged"
"list-entry.created"
"note.created"
"task.created"
// ... plus filtering support

V2 webhooks support event filtering to reduce volume:

await client.post("/webhooks", {
  target_url: "https://yourapp.com/webhooks/attio",
  subscriptions: [
    {
      event_type: "record.updated",
      filter: {
        // Only trigger for updates to the "stage" attribute on deals
        $and: [
          { object: { $eq: "deals" } },
          { attribute: { $eq: "stage" } },
        ],
      },
    },
  ],
});

Community SDK Migration

Since there is no official Attio SDK, you may be using community packages:

attio-js (most popular community SDK)

# Check current version
npm list attio-js

# Upgrade
npm install attio-js@latest
// attio-js uses the v2 API natively
import { AttioClient } from "attio-js";

const client = new AttioClient({ accessToken: process.env.ATTIO_API_KEY });
const people = await client.records.query("people", { limit: 10 });

Direct fetch (recommended for control)

No upgrade risk -- you control the endpoint URLs directly. See attio-sdk-patterns for a typed wrapper.

Detecting API Changes

Attio does not publish a traditional changelog for the REST API. Monitor for changes:

// Save the OpenAPI spec hash and check periodically
import crypto from "crypto";

async function checkForApiChanges(): Promise<boolean> {
  const spec = await fetch("https://docs.attio.com/openapi.json").then(r => r.text());
  const hash = crypto.createHash("sha256").update(spec).digest("hex");

  const previousHash = await readStoredHash(); // From file or DB
  if (previousHash && hash !== previousHash) {
    console.warn("Attio OpenAPI spec changed! Review for breaking changes.");
    await storeHash(hash);
    return true;
  }
  await storeHash(hash);
  return false;
}

Migration Checklist

[ ] Base URL updated to /v2
[ ] Object references use slugs instead of UUIDs where possible
[ ] Record queries migrated from GET to POST with filter body
[ ] Record creation uses data.values wrapper with arrays
[ ] Webhook subscriptions recreated with v2 event types
[ ] Webhook handlers updated for v2 payload format
[ ] Pagination migrated from page/per_page to limit/offset
[ ] Error handling updated for v2 error response format
[ ] Tests updated and passing against v2 endpoints
[ ] OpenAPI spec monitoring configured for future changes

Error Handling

Migration issueSymptomFix
Old v1 URL404 on all callsUpdate base URL to /v2
UUID instead of slug404 on object endpointsUse api_slug from GET /v2/objects
Flat values (v1 format)422 validation errorWrap in { data: { values: { ... } } }
Old webhook event typesWebhook never firesRecreate with v2 event types
Old pagination paramsIgnored, only first page returnedSwitch to limit + offset

Resources

Next Steps

For CI integration during upgrades, see attio-ci-integration.

┌ stats

installs/wk0
░░░░░░░░░░
github stars1.7K
██████████
first seenMar 23, 2026
└────────────

┌ repo

jeremylongshore/claude-code-plugins-plus-skills
by jeremylongshore
└────────────