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.

From issuing an API key to putting it in my-app
The service'swebsiteCreate an accountRegistered asa userIts dashboardIssue a newkeyYou receive onelong stringYour own serverWrite it in .envCan be attachedto calls
Read from the top down. The left column is where you work, the middle is what you do, and the right is what you get there. You create the key in the provider's dashboard, and it goes inside your own server.

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.

One request that my-app sends to the payment service
One request sent by my-app
Line 1 — method and URL
  • POST https://api.pay.example.com/v1/charges
  • Only where it goes and what it does there
From line 2 — request headers
  • The API key goes here
  • Authorization: Bearer sk_live_a1b2
  • One name-and-value pair per line
After a blank line — the body
  • The data you send, as JSON
  • Often empty for calls that only fetch
The outer frame is one request. Putting the key in the URL leaves it in the provider's records and your own server's records, so it goes in the header in the middle.

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 didAuthorization headerWhat the payment service doesWhat comes back
Fetch the list of paymentsBearer sk_live_a1b2Determines the owner and checks the scope200 and JSON
Fetch the list of paymentsForgot to attach itCannot determine the owner401 comes back
Create one paymentBearer sk_read_9f3cOnly reading is allowed403 comes back
Call many times in a short spanBearer sk_live_a1b2Counts the calls per owner429 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.

Where my-app's files end up
All of my-app
Inside your own server (never reaches the device)
.env
  • PAY_API_KEY=sk_live_a1b2
  • Kept in a file separate from the code
server.js
  • Attaches the key read from .env to the call
  • The file itself is never sent to the device
What reaches the browser (anyone can read it)
  • 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
The files in the upper frame never leave your own server. The files in the lower frame are sent to the user's device as they are.

Even for the same key, where you write it changes who can read it.

Where the key is writtenReaches the device?Who can read itWhat happens
.env (inside the server)Does not reach itOnly people with server accessWorks as contracted
script.jsReaches itEveryone who opens the pageOthers can call as you
A published server.jsDoes not reach it, but is publicEveryone who sees the codeCannot 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.

What you can do about a key that has gotten out
sk_live_a1b2has gotten outDelete that linefrom the codeRevoke it inthe dashboardThe value readstill worksCalls with thatkey are refused
The left one only deletes the characters you wrote. The right one makes the key itself unusable on the payment service's side. Only the right one gets calls refused.

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.

Which side sends the request, across three approaches
=ApproachWhich side sendsWhat you set upin advanceCall the APImy-app →payment serviceEndpoint andAPI keyAsk repeatedlyif it is donemy-app →payment serviceHow often to askWebhookPayment service →my-appmy-app's URL fornotifications
The top two rows share the same direction, so they are joined by a double line. Only the webhook goes from the payment service to my-app.

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.

The notifications that arrive from a single payment
A user makesone paymentPayment completedReceipt createdRefunded latermy-app's/webhooks/payment
Even for a single payment, a separate notification arrives for each event. They all arrive at the one URL you registered.

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.

One notification that arrived from the payment service
One request that my-app received
Line 1 — method and URL
  • POST https://my-app.example.com/webhooks/payment
  • The URL you created and registered
From line 2 — request headers
  • The signature goes here
  • The ID that identifies a repeated notification is often here too
After a blank line — the body
  • Which event it is, written as JSON
  • The payment ID and its status are here too
The outer frame is one received request. The signature goes in the header, and the body says which event it is.

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.

QUIZ

Knowledge Check

Answer each question one by one.

Q1When you send a request with an API key attached, what can the external service determine?

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?