Most of a modern app is other people's software. Payments are Stripe, email is Resend, the clever text feature is OpenAI's API. You do not build any of it. What you do build is the thin, fragile seam where your server talks to theirs, and that seam is where the outages and the leaked keys live.
The idea
An Application programming interfaceA defined set of requests one program can make to another. It is a menu of things you are allowed to ask for. is how one program asks another to do something. Your server sends a request to an address the other company publishes, attaches an A long secret string that proves the request is coming from your account. Anyone holding it can act as you. to prove who is asking, and gets an answer back. That key is a password with a spending limit attached, the other company's servers are not always up, and both facts have to be designed for rather than discovered.
YOUR SERVER SOMEONE ELSE'S SERVER
+---------------------+ POST /v1/charges +----------------------+
| checkout handler | -------------------> | Stripe |
| | Authorization: | |
| key from an env | Bearer sk_live_... | checks the key |
| variable, never | | checks rate limits |
| in the repo | <------------------- | does the work |
+---------------------+ 200 / 429 / 503 +----------------------+
|
+-------------------+-------------------+
| | |
200 ok 429 too many 503 / timeout
continue requests they are down
wait and retry show a real error,
with a growing do not silently
delay pretend it workedHow it works
The key identifies your account and carries permissions. Stripe issues a publishable key that is safe in browser code and a secret key that is not, and a secret key in a frontend file lets a stranger issue refunds from your balance. Most services also let you scope a key down: read-only, one product, one domain. Take the narrowest one that does the job.
Rate limits are the second surprise. Every service caps how many requests you may make per minute,
and OpenAI additionally caps tokens per minute, so a loop that calls it once per row will stop
working somewhere around row two hundred. The signal is HTTP status 429, and the fix is waiting
longer between retries rather than retrying harder.
Then there is failure. A request can succeed slowly, fail cleanly, or time out with you never learning whether the other side did the work. That last case is why payment and email calls need to be safe to repeat: if you retry a charge blindly, you charge twice.
What to do
- Keep every key in an environment variable, add
.envto.gitignore, and use separate test and live keys. Stripe's test keys start withsk_test_, which makes a mistake visible on sight. - Call third-party services from your server, never from browser code. The one exception is a key the vendor explicitly publishes as public, like Stripe's publishable key.
- Decide what the user sees when the call fails, before you ship. "Payment could not be completed, you were not charged" is a design decision, and the default is a blank screen.
- Rotate a key the moment it might have leaked. Every dashboard has the button, and it takes a minute.
Where it breaks
Your uptime is now the product of everyone else's. If Resend is down your signup email does not arrive, and no amount of correct code on your side changes that. Vendors also change: prices rise, endpoints get deprecated, an API version you depend on gets retired with six months of notice you did not read. And an AI assistant writing integration code often works from the version it was trained on, which for a service that ships weekly can be a year stale. Check the current docs page for anything that moves money.