Airtable
Airtable's Web API is clean. One POST creates a record and returns its id; wrong input is a named, typed error (unknown field -> 422 UNKNOWN_FIELD_NAME; a string in a number column -> 422 INVALID_VALUE_FOR_COLUMN); and a chain works - create, GET by id, PATCH by id, read back. Strict by default with an opt-in escape: '12' into a number field 422s, but typecast:true coerces it. Idempotency: two identical POSTs create two records, but a PATCH with performUpsert + fieldsToMergeOn:['Name'] updates the existing row instead of duplicating. Watch-out: POST {fields:{}} silently creates a blank row.
Cross-validated onClaude Opus 4.8
The five tasks
Following a multi-step flowNailed it
Getting started from the docs aloneNailed it
Fixing its own mistake after an errorNailed it
Handling an unclear edge caseNailed it
Not double-charging on a retryHalf-nailed it
Here’s the receipt — what actually happened, not our summary of it.
Naive path duplicates: two identical POST {Name:'idem same', Priority:5} -> two different ids (recv0NJIG7VsbzwuT, recgYKRMv6muOo0D4). Safe path exists: PATCH with {performUpsert:{fieldsToMergeOn:['Name']}, records:[...]} - the first upsert on Name 'upsert-key-01' created it, and a second upsert with the same Name returned createdRecords:[] and updatedRecords:['recASPOiN71mhVtmc'] (updated in place, no duplicate). Retries are safe only via the opt-in upsert.See everything the AI did (5 steps)
t1 Claude Opus 4.8 POST /v0/{base}/{table} (create record) → 200 record id
t2 Claude Opus 4.8 POST unknown field ; POST string in number column → 422 UNKNOWN_FIELD_NAME ; 422 INVALID_VALUE_FOR_COLUMN
t3 Claude Opus 4.8 POST -> GET /{id} -> PATCH /{id} -> GET /{id} → create/read/update/verify chain ok
t4 Claude Opus 4.8 POST typecast:true string->number ; POST empty {fields:{}} → 200 coerced to 12 ; 200 blank record created
t5 Claude Opus 4.8 POST x2 same fields ; PATCH performUpsert merge on Name x2 → two ids (duplicate) ; second upsert updated not createdTested 2026-07-04 with Claude Opus 4.8 agents · request a re-test