Q1When you send a request with an API key attached, what can the external service determine?
API Keys and External Services — What a Key Protects, and Webhooks
This article is part of the IT Foundations course, which builds up from scratch the practical IT knowledge you need at a minimum for programming and vibe coding.
An API key is a string that shows whose call it is. Diagrams cover where you may store it and the direction of a webhook, a request sent from the external service to your server.
This article covers two things: the API key you use when calling an external service, and the webhook the external service calls from its side.
You will see where a key is issued, what the string indicates, and where to put it so that no one else can read it.
- Where you issue an API key, and where in the call you attach it
- That a key is a string showing who owns the call, and how that relates to usage-based billing
- Where a key may be stored, and what happens if you write it somewhere that reaches the browser
- The webhook called from the external service's side, and verifying the signature
You issue an API key in the dashboard of the external service you use
To call an external service, you first receive an API key — a string that tells the provider which user a call belongs to — from the provider.
It is not a value you decide; the service you use issues it and hands it to you.
Whether it is payments, maps, or text generation, the sequence — sign up, issue a key, put it on your own server — stays the same.
Copy the issued string on the spot and put it in the .env file on your own server.
Most external services use usage-based billing (you pay for what you use), charging by the number of calls and the volume of data sent and received.
If a key gets out, the calls other people make go on the owner's bill too.
An API key is a string that shows which user a call belongs to
A payment service's API is a public URL, so it needs a way to know whose call has arrived.
You attach it not to the URL but to the request header — the part, separate from the body, that lists name-and-value pairs such as the sender and the format.
- POST https://api.pay.example.com/v1/charges
- Only where it goes and what it does there
- The API key goes here
- Authorization: Bearer sk_live_a1b2
- One name-and-value pair per line
- The data you send, as JSON
- Often empty for calls that only fetch
The second line in the middle frame is the line where the key goes.
Authorization is the header name, Bearer is the word for how the value is passed, and what follows is the key's value.
The provider decides both this name and this format.
From the key it receives, the payment service determines three things: the owner, the operations allowed, and the call count.
| What my-app did | Authorization header | What the payment service does | What comes back |
|---|---|---|---|
| Fetch the list of payments | Bearer sk_live_a1b2 | Determines the owner and checks the scope | 200 and JSON |
| Fetch the list of payments | Forgot to attach it | Cannot determine the owner | 401 comes back |
| Create one payment | Bearer sk_read_9f3c | Only reading is allowed | 403 comes back |
| Call many times in a short span | Bearer sk_live_a1b2 | Counts the calls per owner | 429 comes back |
On a 401, check the key value and the header name; on a 403, check which operations are allowed.
The limit in the fourth row is the rate limit — the maximum number of calls accepted within a given period — and going over it returns 429.
A key is a string that shows who owns a call
An API key is a string whose only job is to say whose call this is.
The payment service reads the key to check the owner and the operations allowed and counts that person's calls, so a missing key returns 401, an operation that is not allowed returns 403, and too many calls returns 429.
Keep the key out of code that reaches the browser, and only inside your own server
Anyone who holds the key can make calls as its owner.
There is no screen that checks whether they are the real owner.
- PAY_API_KEY=sk_live_a1b2
- Kept in a file separate from the code
- Attaches the key read from .env to the call
- The file itself is never sent to the device
- index.html and script.js
- Asks your own server instead of calling the payment service directly
- Holds only keys issued for use from the browser
Even for the same key, where you write it changes who can read it.
| Where the key is written | Reaches the device? | Who can read it | What happens |
|---|---|---|---|
| .env (inside the server) | Does not reach it | Only people with server access | Works as contracted |
| script.js | Reaches it | Everyone who opens the page | Others can call as you |
| A published server.js | Does not reach it, but is public | Everyone who sees the code | Cannot be erased from Git history |
The more people a row lets read the key, the closer it comes to others being able to call as you.
The third row never reaches the browser, but once it is recorded in Git, it stays readable in the change history even if you delete it later.
When instructions say "put it in .env," they are telling you to use the placement in the first row.
There are two things you can do about a key that has gotten out, and they do not have the same effect.
The value someone read stays valid, and it also remains in Git's change history.
The right one is revoking — the provider putting an already-issued key into an unusable state.
Keep the key only where it never reaches a device
Anyone who holds the key can use it as its owner.
So keep it out of files that reach the user's device and out of code you publish, and when it slips out, revoke it and issue a new key instead of deleting the characters you wrote.
A webhook is the mechanism by which the external service calls your own server
A completed payment or a refund is an event whose timing the caller cannot know.
Asking over and over adds wasted calls and delays how soon you notice.
A webhook — the mechanism where the external service sends a request to a registered URL when a specified event happens — reverses this direction.
Only the webhook in the third row goes from the payment service to my-app.
All you set up is one URL, registered in advance, that receives the notifications.
A separate notification arrives when the payment completes, when the receipt is created, and when a refund happens.
Which event a notification is for is written in the request body.
Arrow 1 is the direction where you make the call, and arrow 2 is the direction where the external service calls you.
The key goes only to the payment service, and never reaches the user's device.
A webhook is a call in the opposite direction
A webhook is a call in which the payment service calls my-app.
It exists so that events whose timing you cannot predict are reported the moment they happen, and all you set up is one URL that receives the notifications, created and registered in advance.
Anyone can call the URL that receives notifications, so verify the signature before processing
The URL that receives notifications is public, so someone other than the payment service can send to that same URL.
What prevents this is the signature — a value calculated from a string only the sender and the receiver know, attached to the request, which the receiver checks by running the same calculation.
- POST https://my-app.example.com/webhooks/payment
- The URL you created and registered
- The signature goes here
- The ID that identifies a repeated notification is often here too
- Which event it is, written as JSON
- The payment ID and its status are here too
Verify the signature in the header before reading the contents.
When you do not return a success, the sender sends the same notification again, and that is a retry.
Record the ID of each notification and skip the ones you have already processed.
Like the API key, the string used for verification stays out of the code, and you check how to attach it in the API documentation.
When a guide says "verify the signature," this check is what it means.
Verify before reading, and prepare for a second delivery
The URL that receives notifications is public, so the fact that a request arrived tells you nothing about the sender.
Verify the signature before reading the body, and if it matches, update the record and return a success status code, then remember the IDs of the notifications you have processed so that a second delivery is skipped.
Knowledge Check
Answer each question one by one.
Q2What is the problem with writing an API key in code that reaches the browser?
Q3When a webhook notification arrives, what do you do before reading its contents?