> attio-common-errors
Diagnose and fix the top Attio REST API errors by HTTP status code. Real error response formats, actual error codes, and proven fixes. Trigger: "attio error", "fix attio", "attio not working", "attio 429", "attio 403", "attio 422", "debug attio".
curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/attio-common-errors?format=md"Attio Common Errors
Overview
Every Attio API error returns a consistent JSON body. This skill covers the real error codes, response format, and proven solutions for each.
Attio Error Response Format
All errors from https://api.attio.com/v2 return this structure:
{
"status_code": 429,
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, please try again later"
}
Fields: status_code (HTTP status), type (error category), code (specific code), message (human-readable).
Error Reference
400 Bad Request -- invalid_request
{ "status_code": 400, "type": "invalid_request_error", "code": "invalid_request", "message": "..." }
Common causes and fixes:
| Message pattern | Cause | Fix |
|---|---|---|
Invalid value for attribute | Wrong type for attribute slug | Check attribute type with GET /v2/objects/{obj}/attributes |
Cannot query historic values | Used history param on unsupported type | Remove show_historic for that attribute |
Missing required field | Required attribute not provided | Check is_required on attribute definition |
Invalid filter format | Malformed filter object | Use shorthand { "email": "x" } or verbose { "$and": [...] } |
Diagnostic:
# List attributes to verify types
curl -s https://api.attio.com/v2/objects/people/attributes \
-H "Authorization: Bearer ${ATTIO_API_KEY}" \
| jq '.data[] | {slug: .api_slug, type: .type, required: .is_required}'
401 Unauthorized -- authentication_error
{ "status_code": 401, "type": "authentication_error", "code": "invalid_api_key", "message": "..." }
| Cause | Fix |
|---|---|
Missing Authorization header | Add Authorization: Bearer sk_... |
| Token revoked or deleted | Generate new token in Attio dashboard |
| Malformed header | Ensure format is Bearer <token> (one space, no quotes) |
Diagnostic:
# Verify token works
curl -s -o /dev/null -w "%{http_code}" \
https://api.attio.com/v2/objects \
-H "Authorization: Bearer ${ATTIO_API_KEY}"
# Should return 200
403 Forbidden -- insufficient_scopes
{ "status_code": 403, "type": "authorization_error", "code": "insufficient_scopes",
"message": "Token requires 'record_permission:read-write' scope" }
| Operation | Required scopes |
|---|---|
| List/get records | object_configuration:read + record_permission:read |
| Create/update records | object_configuration:read + record_permission:read-write |
| List entries | object_configuration:read + record_permission:read + list_entry:read |
| Create/update entries | Above + list_entry:read-write |
| Create notes | note:read-write + object_configuration:read + record_permission:read |
| List tasks | task:read + object_configuration:read + record_permission:read + user_management:read |
| Manage webhooks | webhook:read-write |
Fix: Edit token in Settings > Developers > Access tokens, add missing scope, save. No need to regenerate.
404 Not Found -- not_found
{ "status_code": 404, "type": "not_found_error", "code": "not_found", "message": "..." }
| Cause | Fix |
|---|---|
| Wrong object slug | Verify with GET /v2/objects -- use api_slug field |
| Invalid record_id | Record may have been deleted or merged |
| Wrong list slug | Verify with GET /v2/lists |
| Typo in endpoint path | Check path starts with /v2/ |
409 Conflict -- conflict
Occurs when creating a record with a value that conflicts with an existing unique attribute (e.g., duplicate email or domain).
Fix: Use PUT (assert) instead of POST to upsert:
// Assert: create or update matching record
await client.put("/objects/people/records", {
data: {
values: {
email_addresses: ["existing@example.com"],
name: [{ first_name: "Updated", last_name: "Name" }],
},
},
});
422 Unprocessable Entity -- validation_error
| Message pattern | Cause | Fix |
|---|---|---|
Invalid email address | Malformed email string | Validate email format before sending |
Invalid phone number | Not E.164 format | Prefix with country code: +14155551234 |
Unknown attribute | Attribute slug does not exist | List attributes first |
Invalid record reference | target_record_id doesn't exist | Verify record exists first |
429 Too Many Requests -- rate_limit_exceeded
{
"status_code": 429,
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, please try again later"
}
Attio uses a sliding window algorithm with a 10-second window. The Retry-After response header contains a date (usually the next second).
Immediate fix:
if (res.status === 429) {
const retryAfter = res.headers.get("Retry-After");
const waitMs = retryAfter
? new Date(retryAfter).getTime() - Date.now()
: 1000;
await new Promise((r) => setTimeout(r, Math.max(waitMs, 100)));
// Retry the request
}
See attio-rate-limits for full backoff and queue patterns.
500+ Server Error
Rare, but Attio may reduce rate limits during incidents. Always implement retry for 5xx.
Check: status.attio.com
Quick Diagnostic Script
#!/bin/bash
echo "=== Attio Diagnostic ==="
echo -n "Auth: "
curl -s -o /dev/null -w "%{http_code}" \
https://api.attio.com/v2/objects \
-H "Authorization: Bearer ${ATTIO_API_KEY}"
echo ""
echo -n "Status page: "
curl -s https://status.attio.com/api/v2/status.json | jq -r '.status.description'
echo "Objects:"
curl -s https://api.attio.com/v2/objects \
-H "Authorization: Bearer ${ATTIO_API_KEY}" \
| jq -r '.data[].api_slug' 2>/dev/null || echo "FAILED"
Error Handling Pattern
import { AttioApiError } from "./client";
async function handleAttioError(err: AttioApiError): Promise<void> {
switch (err.statusCode) {
case 401: throw new Error("Attio auth failed -- check ATTIO_API_KEY");
case 403: throw new Error(`Missing scope: ${err.message}`);
case 404: console.warn("Resource not found, may have been deleted"); break;
case 409: console.warn("Conflict -- use PUT to upsert instead"); break;
case 429: /* handled by retry wrapper */ break;
default: throw err;
}
}
Resources
Next Steps
For evidence collection, see attio-debug-bundle. For retry patterns, see attio-rate-limits.
> related_skills --same-repo
> fathom-cost-tuning
Optimize Fathom API usage and plan selection. Trigger with phrases like "fathom cost", "fathom pricing", "fathom plan".
> fathom-core-workflow-b
Sync Fathom meeting data to CRM and build automated follow-up workflows. Use when integrating Fathom with Salesforce, HubSpot, or custom CRMs, or creating automated post-meeting email summaries. Trigger with phrases like "fathom crm sync", "fathom salesforce", "fathom follow-up", "fathom post-meeting workflow".
> fathom-core-workflow-a
Build a meeting analytics pipeline with Fathom transcripts and summaries. Use when extracting insights from meetings, building CRM sync, or creating automated meeting follow-up workflows. Trigger with phrases like "fathom analytics", "fathom meeting pipeline", "fathom transcript analysis", "fathom action items sync".
> fathom-common-errors
Diagnose and fix Fathom API errors including auth failures and missing data. Use when API calls fail, transcripts are empty, or webhooks are not firing. Trigger with phrases like "fathom error", "fathom not working", "fathom api failure", "fix fathom".