Every integration is a treasure map drawn by someone who didn't quite finish the legend.
We've been stitching together a small fleet of services — print-on-demand, an online marketplace, an email list, a few AI providers, analytics — and every one of them had at least one barnacle nobody mentions in the docs. Some traps cost us a single hour. One cost us a week. This is the field log: what worked, what didn't, and where the wrecks are.
The crew of services on this ship. Eight separate auth flows, eight different ideas about pagination, and eight definitions of what "synced" actually means: a print-on-demand fulfillment provider, an online marketplace, multiple AI providers, an edge/DNS provider, a knowledge tool, a mailing list, a social/scraping provider, and a git host.
1. Singular vs. plural endpoints. The single most expensive bug of the build. A core endpoint of one service exists in singular form (works) and plural form (404). Both feel obvious; both look documented; only one exists. We spent the better part of a day convinced this update path simply wasn't supported, when in reality we were a single missing s away from done. Lesson: when an endpoint 404s, before assuming the feature doesn't exist, try the other form of the noun.
2. The colon trap. One service's API key is actually two strings — a public keystring and a shared secret — that must be combined with a colon in a single header. Pass just one, and you get a confident-looking 403 that sounds like the wrong key but is really a "you didn't read paragraph three" error. Lesson: when 403 comes back fast and clean, the credentials are probably valid and you've assembled them wrong.
3. Read-mostly APIs are a category. Two of our integrations are the same service in two flavors: one where you fully own the catalog (POST anything), and one where the catalog lives in someone else's marketplace (POST almost nothing). The docs don't loudly distinguish these — the same endpoint will return 200 for one customer type and 405 for another. We ended up driving a real browser to do anything the API refused. Lesson: probe every CRUD verb before committing to an architecture.
4. The full-matrix update. One marketplace's inventory endpoint will let you change individual variants — as long as you submit every single variant in the same call, fully populated, with the right property metadata, and a "readiness state" field that isn't mentioned in half the example payloads. Five attempts; six required fields. Lesson: when an API insists on a full payload, fetch the current state first, mutate, send the result back. Don't try to write a "minimal patch" — that's a different API.
5. The token-refresh dance. One service hands you a 60-minute access token. Whatever script you wrote that loops for two hours will silently start failing at minute 61, and the error message will look like an auth bug, not an expiry bug. Lesson: wrap every short-lived token in a tiny refresh helper. Run it at the top of any script.
What actually worked. A single small request helper that auto-handles rate-limit 429s. Verify-after-every-write — re-reading the resource after every successful API call caught more bugs than every other technique combined. A --dry flag on every destructive script prevented at least three "I am ten seconds from a refactor I can't undo" moments. Screenshots from the browser as a debugger — when UI automation behaved oddly, a single full-page screenshot revealed a popup modal the script couldn't see.
Where we're still circling the reef. There's exactly one component we can't automate: a Vue-rendered radio button that ignores synthetic mouse events, synthetic keyboard events, dispatched click events, and direct element.click() calls. A human click works in half a second. We've made peace with that one click for now. If you've solved this in the wild, please send a postcard.
Integrations aren't really about APIs. They're about understanding the shape of someone else's invariants — what they think a product is, what they think "synced" means, what fields are load-bearing and what fields are decorative. Every reef on our map was somebody else's invariant we hadn't read closely enough. We row on. More logs as we hit them.
— the crew