I have had the displeasure of integrating with a few different APIs over the past few weeks while working to automate Vori’s onboarding flow. These APIs have been for systems including contract rendering and e-signature collection, invoicing/billing systems, CRMs, card processors and gateways, and card terminal providers.
They all kinda suck in their own ways. This would be somewhat acceptable if they were doing novel work in new fields, but these are all APIs for solutions that have existed for a decade or more. It’s also frustrating because the developers could simply copy existing, better, APIs and practices, and come out ahead!
Here are some of my pet peeves.
You’ve already messed up if I need to log in to read your API documentation. Kudos for actually writing the docs, but why do I need to log in!? I have to interrupt my flow to fill out a form or send an email to your support team, and wait a few hours or days, just to read documentation!?
I complained to one team about this and they agreed that gated documentation is not ideal. However, an executive wanted lead tracking. WTF!? We are already customers. The lead is closed-won. There’s nothing else to track!
This is worse with agentic development. I prefer to provide links to docs so the agent can explore the schema, build a client, and integrate. That flow is disrupted by gating. I now have to download the docs in some fashion—fortunately the offender in question offers a Markdown option—to provide for the agent. This needs to be done for every endpoint!
Just remove the authentication requirement for docs. You’re wasting your own time and resources just so everyone else can waste their own time and resources. This is a lose-lose scenario.
Show of hands. Who likes handwriting API clients? If your hand is up, I don’t believe you. The OpenAPI specification has existed for 15 years. Publishing an API without one is just disrespectful at this point. Why don’t you like me? Why do you want to make my life harder when I’m trying to give you money? Help me help you. Give me a spec so I can generate a typed client and focus on my business.
Oh, “here’s a Postman collection,” you say? I guess something is better than nothing, but now I have to figure out how to convert that to an OpenAPI spec. Why are we wasting time with an inferior format? Give me the good stuff!
This is similar to gated docs. APIs need credentials. Duh. Let me generate/rotate them on my own. Why do I need to wait multiple weeks for the IT team to generate credentials, flip a flag, or whatever? Stuff happens. Sometimes we need to rotate credentials. Don’t make me file a support ticket for a potential security incident! That means an issue that crops up on Saturday probably isn’t getting fixed until mid-day Monday when someone reads the ticket.
So you created self-serve credential issuance. Cool. But wait! Now you tell me the credential is associated with the identity of the person that created the credential. Meaning…all logs are associated with that person, so it’s impossible to discern between API calls from our backend applications and calls made by that person in your web app? Meaning…that person is the only one that can rotate the credentials, and we can’t simply follow the bad practice of sharing credentials because your application requires SSO and we have to draw the line somewhere at account sharing? Meaning…deactivating that person’s account will almost assuredly result in an incident?
I’m actively migrating away from an e-sign provider that does this because *I* am the person who set up the account, and am currently on vacation, and blocking folks from seeing contracts, because of course I want all the contracts sent to grocers to be associated with my account! My list of questions and criteria for vendors increases.
Webhooks are great for building realtime-ish workflows. Love ‘em. I have no love for the aforementioned e-sign provider that wants to verify webhook endpoints before saving them. “What is this verification”, you ask? It’s simple (and stupid): the provider sends a payload to the endpoint and only saves the new webhook configuration if the endpoint returns a successful response.
One of the first sections in any webhook integration guide covers security. Always validate the payload with a shared secret, and reject invalid payloads without processing. Well…it’s hard to validate without that shared secret, and the provider won’t give me a shared secret until the endpoint is verified to work. 🙃
This was the ridiculous workaround for a problem that doesn’t need to exist:
- Create a webhook for a known URL that always returns a 200 response for POST, such as https://echo.free.beeceptor.com.
- Store the shared secret in the secret manager.
- Update the webhook with the correct URL.
- Pray you never need to rotate the secret.
I wrote a support ticket for this, and the folks who responded didn’t quite understand why this was a problematic workflow. They do understand that I am migrating away from their product, however, and suddenly want to chat to get feedback.
It’s worth noting that I haven’t even discussed the API schemas and resources. Most companies get this right for their RESTful APIs with understandable nouns and verbs that define the business concepts and actions. Older payments companies continue to struggle with this for some inexplicable reason despite having over 15 years to just copy Stripe. Seriously, just copy the Stripe API. We spent a lot of time and energy building it. It’s good. Take it.
I’m just happy folks are building APIs, even with the horrible developer experience. We’ve largely eliminated a 40+ step process in about four weeks. Sure, agents wrote the code in like 4 hours and most of that four weeks was waiting for credentials, but progress is progress.