Skip to content
Prompt Words
History

Backend

How the server behaves.

0 of 19 learned

UI and server 0 / 11

Where the screen meets the server.

Cursor pagination

Also called: keyset pagination, cursor-based pagination, seek method.

Each “Load more” asks for the posts after the last one you have (after=p_28). New posts at the top don't shift anything, so nothing repeats or goes missing.

Page 1

Each page asks for the items after the last one you already have, using a cursor such as that item's id. New items at the top don't shift anything, so nothing repeats or goes missing, but you can't jump straight to page 7.

Say it in a prompt

Paginate GET /api/posts with a cursor. Accept limit (default 20, max 100) and after=<post id>, return the posts older than that post sorted by created_at and then id, plus next_cursor and has_more. The feed shows a "Load more" button that sends the last next_cursor.

Seen on

  • Stripe API: List endpoints take limit, starting_after and ending_before (an object id) and answer with has_more.
  • Slack Web API: Paginated methods return response_metadata.next_cursor, which you send back as cursor to get the next portion.

You might describe it as

  • load more button that continues from the last item
  • infinite feed that never shows the same post twice
  • next page starts right after the last thing I saw

Not to be confused with

  • Offset pagination

    Cursor pagination continues after the last item you have; offset pagination skips a count of rows, and that count shifts when new rows arrive.

Debounce

Also called: debouncing, wait until typing stops, input delay.

Type in the box, or press the button. With debounce on, the search waits until you stop typing for 300 ms and sends one request instead of one per key.

Type to search

Idle

Wait until something has stopped happening for a short time, then act once. A search box with a 300 ms debounce sends one request after you stop typing, instead of one for every key.

Say it in a prompt

Debounce the product search box by 300 ms so GET /api/search?q= is only called after the user stops typing for 300 ms. Skip queries shorter than 2 characters, and cancel the previous request so an old answer never replaces a newer one.

Seen on

  • Lodash: _.debounce delays a function until a set number of milliseconds have passed since it was last called, with leading, trailing and maxWait options.

You might describe it as

  • search that waits until I stop typing
  • don't send a request for every key I press
  • wait a moment after the last keystroke, then search

Not to be confused with

  • Throttle

    Debounce waits for a pause and fires once at the end; throttle keeps firing, but at most once per interval.

Offset pagination

Also called: page-based pagination, limit and offset, numbered pages.

Each page skips a number of rows (page 2 = skip 3, take 3), so you can jump to any page. But when a new order arrives, every row moves down one, and the next page repeats one you already saw.

Page 1

Each page skips a number of rows and takes the next few, so page 3 with 20 per page means skip 40, take 20. You can jump to any page and show a total, but new or deleted rows shift everything, and big skips get slow.

Say it in a prompt

Paginate GET /api/orders with page and per_page (default 25, max 100) using LIMIT and OFFSET, ordered by created_at DESC and then id DESC. Return total and total_pages so the table can show numbered pages 1 to N with Previous and Next buttons.

Seen on

  • WordPress REST API: Collections take page, per_page and offset parameters and send X-WP-Total and X-WP-TotalPages headers with the totals.
  • GitHub REST API: Many list endpoints use page and per_page, with a link header pointing to the next, previous, first and last pages.

You might describe it as

  • numbered pages at the bottom of the table
  • jump straight to page 5 of the results
  • the same item shows up again on the next page

Not to be confused with

  • Cursor pagination

    Offset pagination counts rows to skip, so you can jump to any page; cursor pagination continues from the last item, can't jump, but never repeats.

Optimistic update

Also called: optimistic UI, instant update, update first, confirm later.

Tap the heart: it fills at once, and the server answers 1.2 s later. Turn on “Server fails” to see the like roll back.

Idle

The screen shows the result of your action right away, before the server has confirmed it. If the server then says no, the screen goes back to how it was and tells you it didn't work.

Say it in a prompt

When the user taps Like, fill the heart and add 1 to the count immediately, then send POST /posts/:id/like. If the request fails or takes longer than 5 s, put the heart and the count back to what they were and show "Couldn't save your like" under the post for 4 s.

Seen on

  • React: The useOptimistic hook shows a temporary value while a request is running; the docs use a like button as the example.
  • TanStack Query: Its optimistic updates guide saves the old data in onMutate and puts it back in onError when the request fails.

You might describe it as

  • like button that changes instantly without waiting for the server
  • the heart fills right away even on slow internet
  • show the change first and undo it if saving fails

Polling

Also called: short polling, periodic refresh, checking for updates.

The page asks the server “anything new?” every 2 s. A message waits on the server until the next check, and most checks find nothing.

Stopped

The page asks the server "anything new?" again and again on a timer, for example every 5 seconds. It is simple, but news arrives up to one interval late, and most requests come back empty.

Say it in a prompt

While the order page is open and the tab is visible, poll GET /api/orders/:id/status every 5 seconds. Send the last ETag in If-None-Match so an unchanged status returns 304, and stop polling once the status is "delivered" or after 10 minutes.

Seen on

  • GitHub REST API: The events API is built for polling: it sends an X-Poll-Interval header with how many seconds to wait, and 304 Not Modified when nothing changed.

You might describe it as

  • page that checks for new messages every few seconds
  • keep asking the server if something changed
  • auto refresh the data on a timer

Not to be confused with

  • Server-sent events (SSE)

    With polling the page keeps asking; with server-sent events the server tells the page the moment something happens.

  • Webhook

    Polling is your code asking again and again; a webhook is the other service calling your server once when something happens.

Rate-limit message (429)

Also called: 429 Too Many Requests, too many requests message, rate limit error, cooldown message.

The server allows 3 codes every 15 s. Tap “Resend code” 4 times: the 4th gets 429 Too Many Requests with Retry-After. The friendly message turns that into plain words and a countdown.

Ready

What the user sees when the server says "too many requests, slow down" (HTTP status 429). A good one says in plain words how long to wait, often with a countdown taken from the Retry-After header.

Say it in a prompt

When POST /login/code/resend returns 429, read the Retry-After header and show "Too many codes asked for. You can ask for a new one in 30 s." under the button. Disable "Resend code" with a live countdown, and enable it again when the countdown reaches 0.

Seen on

  • GitHub REST API: Over the limit it answers 403 or 429, with retry-after or x-ratelimit-reset saying when you may try again.
  • Stripe API: Rate-limited requests get 429 Too Many Requests and a Stripe-Rate-Limited-Reason header that says which limit was hit.

You might describe it as

  • too many attempts, try again in 30 seconds
  • error when I click resend too many times
  • countdown before I can ask for another code

Not to be confused with

  • Retry with backoff

    A rate-limit message tells the person how long to wait; retry with backoff is code that waits and tries again on its own.

Request timeout

Also called: timeout, request deadline, give up after N seconds.

This server hangs and never answers. With a 3 s timeout the page stops waiting and says so. Without one it just keeps loading. Turn off “Server hangs” to see a normal fast answer.

Idle

The longest time the page or the server will wait for an answer before giving up. Without one, a stuck server can leave a spinner turning for minutes.

Say it in a prompt

Give every call to GET /api/reports a 10 s timeout using AbortSignal.timeout(10000). When it times out, stop the spinner, show "This is taking longer than usual" with a Try again button, and log the endpoint and how long it waited.

Seen on

  • MDN Web Docs: AbortSignal.timeout() gives a signal that aborts a fetch after the time you set, failing with a TimeoutError.
  • Stripe Node.js library: Its timeout option sets the most time each request may take; the default is 80000 ms.

You might describe it as

  • loading forever and never finishing
  • give up if the server doesn't answer in a few seconds
  • stop waiting and show a try again message

Not to be confused with

  • Retry with backoff

    A timeout decides when to stop waiting; retry with backoff decides whether and when to try again.

Retry with backoff

Also called: exponential backoff, backoff and retry, retry with jitter.

The server is down for 3 s. With backoff, each retry waits twice as long as the one before (0.5 s, 1 s, 2 s), so the save works on the 4th try. Without it, the page hammers the server every 150 ms.

Idle

When a request fails for a reason that may pass, try again, but wait longer before each new try (for example 1 s, 2 s, then 4 s). A little randomness, called jitter, stops many clients from retrying at the same moment.

Say it in a prompt

Retry failed calls to the shipping API only on network errors, 429 and 5xx responses. Make at most 4 tries, waiting 1 s, 2 s, then 4 s with ±20% random jitter, and use the Retry-After header instead when the server sends one. Show "Still trying…" after the second failure.

Seen on

  • AWS SDKs: The standard retry mode retries throttling and short-lived errors with exponential backoff and jitter, making 3 attempts in total by default.
  • Stripe API: The rate limits guide says to retry 429 responses on an exponential backoff schedule with added randomness.

You might describe it as

  • try again a few times but wait longer each time
  • don't give up on the first network error
  • automatic retries that slow down so the server can recover

Not to be confused with

  • Request timeout

    Backoff decides when to try again after a failure; a timeout decides when to stop waiting for an answer.

  • Idempotent request

    Retry with backoff sends the same request again; an idempotent request is what makes that safe, so a retry can't do the work twice.

  • Rate-limit message (429)

    Backoff is the code slowing down its own retries; a rate-limit message is what a person sees when the server answers 429.

Server-sent events (SSE)

Also called: SSE, EventSource, event stream, server push.

The page opens one stream and keeps it open. The server pushes each message down it the moment it happens. Drop the connection: the browser reconnects and asks for what it missed.

Closed

The page opens one connection that stays open, and the server pushes a message down it each time something happens. It only goes one way, from the server to the page, and the browser reconnects by itself if the connection drops.

Say it in a prompt

Push order status changes with server-sent events: GET /api/orders/:id/events answers with Content-Type text/event-stream, sends each change as an "event: status" message with an id, and sends a ": ping" comment every 15 s. In the browser use EventSource, update the status badge on each event, and let it reconnect with Last-Event-ID so no change is missed.

Seen on

  • Claude API: With "stream": true, the reply arrives as server-sent events such as message_start, content_block_delta and message_stop.
  • MDN Web Docs: The EventSource guide: the server answers with text/event-stream, the connection only goes one way, and the browser reconnects after the retry time.

You might describe it as

  • the server pushes new messages to the page without being asked
  • AI chat answer that streams in piece by piece
  • live feed that updates the moment something happens

Not to be confused with

  • Polling

    Server-sent events keep one connection open and the server pushes; polling sends a new request every few seconds to ask.

  • WebSocket

    Server-sent events only go from the server to the page, over plain HTTP; a WebSocket carries messages both ways.

Throttle

Also called: throttling, at most once per interval, rate-limit a handler.

Scrolling fires about 30 events a second. The page saves your reading position; with throttle on it saves at most once every 500 ms while you keep scrolling.

Idle

Let something run at most once per time interval, however often it is triggered. Scroll events can fire dozens of times a second; throttled to 500 ms, the page saves your reading position only twice a second.

Say it in a prompt

Throttle the scroll handler that saves reading progress to one call per 500 ms, on both the leading and trailing edge, sending PUT /api/articles/:id/progress with the percent read, so the last position is always saved when scrolling stops.

Seen on

  • Lodash: _.throttle makes a function run at most once every set number of milliseconds.

You might describe it as

  • only save every half second while scrolling
  • stop the scroll handler from running hundreds of times
  • limit how often it fires while I keep moving

Not to be confused with

  • Debounce

    Throttle keeps firing at a steady pace while events continue; debounce waits until they stop and fires once.

WebSocket

Also called: WebSockets, WS, socket connection, realtime connection.

One connection stays open and carries messages both ways: yours go up (↑), Sam's typing and replies come down (↓) without the page asking.

Closed

One connection between the page and the server that stays open and carries messages in both directions at any time. It fits chat, multiplayer games and live cursors, where both sides talk often.

Say it in a prompt

Build the chat on a WebSocket at wss://example.com/chat: send each message as JSON {"type":"message","text":"..."}, show "Sam is typing…" when a {"type":"typing"} frame arrives, send a ping every 30 s, and when the socket closes, reconnect after 1 s, 2 s, then 4 s (at most 30 s) and resend any message the server did not confirm.

Seen on

  • Slack: In Socket Mode, Slack sends events to your app over a WebSocket URL instead of calling a public HTTP endpoint.
  • Discord: Bots get real-time events through the Gateway, a WebSocket connection kept alive with regular heartbeats.

You might describe it as

  • chat where messages show up instantly on both sides
  • see when the other person is typing
  • a connection that stays open so both sides can talk

Not to be confused with

  • Server-sent events (SSE)

    A WebSocket lets both sides send at any time; server-sent events only push from the server to the page.

API and data 0 / 8

How requests and stored data behave.

API versioning

Also called: versioned API, v1 and v2 endpoints, API version header.

v2 renamed price to amount. Each app calls its own version, so both keep working. Here the version is in the URL; a header like Api-Version works the same way.

/v1 calls: 0 · /v2 calls: 0 · Times a screen broke: 0

No calls yet. v2 renamed the field price to amount; v1 still sends price.

    No calls

    Giving each shape of an API a version, in the URL like /v2/ or in a header, so old apps keep working when the API changes. Breaking changes go into a new version, and the old one keeps running until an announced end date.

    Say it in a prompt

    Version the public API in the URL. Move today's routes under /v1, build the changed orders response under /v2, keep /v1 running for 12 months, and add a Sunset header with the shutdown date to every /v1 response.

    Vague vs precise prompt

    Vague prompt

    rename the price field to amount in the API

    Typical resultRenames the field everywhere at once. Every mobile app still on an older release breaks the moment the change is deployed.

    Precise prompt

    Add API version 2026-09-01, chosen with an Api-Version header and defaulting to each client's pinned version. Only in that version rename price to amount; older versions keep price. Note it in the changelog and announce an end date for the old version.

    Typical resultOld apps keep getting price, new clients opt in to amount, and nothing breaks on deploy day.

    Seen on

    • Stripe API: Versions are dated, like 2026-08-26.dahlia; each account has a default version, and the Stripe-Version header overrides it for one request.
    • GitHub REST API: The X-GitHub-Api-Version header picks a dated version; requests without it get 2022-11-28.

    You might describe it as

    • change the API without breaking the old app
    • keep v1 working while we build v2
    • old mobile apps still call the old endpoints

    Audit log

    Also called: audit trail, activity log, event history.

    The customers table keeps only the latest plan. The audit log keeps every change: who, what, when, before → after.

    Log entries: 0 · Edits refused: 0

    Customer 42 is on the Basic plan. Nothing has changed yet, so the audit log is empty.

      Entries 0

      A list of who did what and when, written as it happens and never edited, like "Ana changed the plan to Pro on 3 Sep at 14:02". Teams use it to answer "who changed this?" and for security reviews.

      Say it in a prompt

      Write an audit log entry for every permission change: actor, action (role.granted or role.revoked), target user, old and new role, time and IP address. Store entries append-only, never update or delete them, and add GET /api/audit-events with actor, action and date filters for admins.

      Vague vs precise prompt

      Vague prompt

      keep track of changes in the admin panel

      Typical resultAdds an updated_at column and maybe a console.log. You can see that a record changed, but not who changed it, what it was before, or any earlier change.

      Precise prompt

      Add an audit log: for every create, update and delete in the admin panel, insert a row into audit_events with actor_id, action, target type and id, before and after values, IP address and time. The table is insert-only, kept for 1 year, and shown on a filterable Activity page.

      Typical resultAdmins open Activity, filter by person or record, and see lines like "Ana changed plan from Basic to Pro, 3 Sep 14:02" for every change.

      Seen on

      • GitHub: An organization's audit log records the actor, the action (like repo.create) and the time, and keeps the last 180 days.

      You might describe it as

      • history of who changed what
      • see which admin removed the user
      • a record of every change for security

      Not to be confused with

      • Soft delete

        An audit log records who did what and when; a soft delete keeps the deleted row itself so it can be restored.

      Batch endpoint

      Also called: bulk endpoint, batch request, bulk API.

      Import 20 contacts. One by one is 20 round trips. A batch endpoint takes all 20 in one request and answers with a result for each.

      One by onenot run yet

      One batchnot run yet

      Nothing sent yet. 20 contacts are waiting to be imported.

        Idle

        One endpoint that takes many operations in a single request, like "create these 200 contacts", and answers with a result for each one. It saves hundreds of round trips.

        Say it in a prompt

        Add POST /api/tags/batch that accepts up to 100 operations, each {"op":"add" or "remove","itemId":...,"tag":...}. Apply each operation on its own (one savepoint each), so one that fails doesn't undo the others, and return an array of results in the same order, each with its index and status (200, or 4xx with an error message).

        Vague vs precise prompt

        Vague prompt

        let users import their contacts

        Typical resultThe app calls POST /contacts once per contact. 2,000 contacts take minutes, hit the rate limit halfway, and leave a half-finished import.

        Precise prompt

        Add POST /contacts/batch that takes up to 500 contacts, validates each one, saves the valid ones (a bad one doesn't undo the others), and returns a result per item (index, status, id or error). The import screen sends 500 at a time and lists the rows that failed.

        Typical result2,000 contacts go in 4 requests in a few seconds, and the user sees exactly which rows failed and why.

        Seen on

        • Microsoft Graph: JSON batching sends up to 20 requests in one POST to /$batch, and each answer inside the batch has its own status code.

        You might describe it as

        • send 500 items in one request instead of 500 requests
        • bulk import that doesn't hit the rate limit
        • save many things at once

        Not to be confused with

        • N+1 query

          A batch endpoint cuts many HTTP requests down to one; fixing an N+1 query cuts many database queries inside one request.

        Idempotent request

        Also called: idempotency, idempotency key, safe to retry.

        The first reply gets lost, so the app sends the payment again. With an Idempotency-Key the server sends back its saved reply; without one it charges the card again.

        Card charged 0 times

        Nothing paid yet.

          Idle

          A request you can send twice, or ten times, with the same result as sending it once. For payments, the client sends a unique idempotency key, and for a repeated key the server returns the saved first answer instead of charging again.

          Say it in a prompt

          Make POST /api/orders idempotent. The client creates one UUID per checkout and sends it in an Idempotency-Key header. Before creating the order, the server reserves the key with a unique insert (in progress), then saves the response status and body with it for 24 hours. The same key again gets the saved response, a key still in progress gets 409 Conflict, and the same key with a different body gets 422.

          Vague vs precise prompt

          Vague prompt

          make the payment endpoint safe to retry

          Typical resultWraps the call in try/catch and retries it. When the first try actually went through but the answer was lost, the retry charges the card a second time.

          Precise prompt

          Make POST /payments idempotent: require an Idempotency-Key header, reserve the key with a unique insert before charging, store the response with it for 24 hours, and return the stored response for a repeated key.

          Typical resultA repeated request gets the first response back, and the card is charged once, however many times the app retries.

          Seen on

          • Stripe API: POST requests accept an Idempotency-Key header; Stripe saves the first result for that key, returns it for repeats, and may remove keys after 24 hours.

          You might describe it as

          • customer got charged twice when they double clicked pay
          • safe to send the same request again after a timeout
          • pressing submit twice should only create one order

          Not to be confused with

          • Retry with backoff

            An idempotent request makes a repeat harmless; retry with backoff is what sends the repeat.

          • Upsert

            An idempotent request makes a whole API call safe to repeat; an upsert is one database write that inserts or updates a row by its key.

          N+1 query

          Also called: N+1 problem, N plus one queries, query in a loop.

          One GET /orders request returns 5 orders, each with its customer. Inside it, N+1 asks for each customer in its own query; an IN query asks for all 5 at once.

          N+1
          –not run yet
          One IN query
          –not run yet

          Rough time if each query takes about 2 ms.

          Nothing loaded yet. Try both ways and compare the query count.

            Idle

            Code loads a list with one query, then runs one more query for each item in it. 50 posts become 51 database calls, and the page gets slower as the list grows.

            Say it in a prompt

            Fix the N+1 queries on the orders page. It loads 100 orders and then each order's customer and items one at a time; eager-load customers and items so the page makes 3 queries in total, and log a warning in development when one request runs more than 20 queries.

            Vague vs precise prompt

            Vague prompt

            the posts page is slow, make it faster

            Typical resultAdds a cache in front of the page or suggests a bigger database. The page still runs one author query per post, so it slows down again as posts grow.

            Precise prompt

            GET /posts has an N+1 query: 1 query for 50 posts, then 1 per post for its author. Load all authors in one query with WHERE id IN (...), or eager-load them, and add a test that the endpoint runs at most 3 queries.

            Typical resultThe endpoint makes 2 queries however many posts there are, and the test fails if someone puts a query back inside the loop.

            Where it shows up

            • A blog's home page lists 10 posts and then asks the database for each post's author separately: 11 queries where 2 would do.
            • An ORM loads a related record lazily inside a loop, so a page that felt fast with 5 rows in development is slow with 500 rows in production.

            You might describe it as

            • the page gets slower the more items there are
            • hundreds of tiny database queries for one page
            • a database call inside a for loop

            Not to be confused with

            • Batch endpoint

              An N+1 query is too many database calls inside one request; a batch endpoint lets a client send many operations in one HTTP request.

            Soft delete

            Also called: logical delete, trash, deleted_at flag.

            Soft delete sets deleted_at instead of removing the row. The list hides the row, but it stays in the table, so it can be restored. Delete forever, or the cleanup job 30 days later, removes it for real.

            List: 3 projects · Table: 3 rows

            Nothing deleted yet. The list and the table both have 3 projects.

              All live

              Deleting marks a row as deleted, for example by setting a deleted_at date, instead of removing it. Normal queries hide it, and it can be restored or cleaned up for good later.

              Say it in a prompt

              Use soft delete for comments. Set deleted_at instead of removing the row, add deleted_at IS NULL to every list and count query, show "This comment was deleted" where a reply still points to it, and let admins restore it for 14 days.

              Vague vs precise prompt

              Vague prompt

              add a delete button for projects

              Typical resultAdds DELETE /projects/:id that runs a SQL DELETE. One wrong click removes the project, its tasks and its history for good, with no way back.

              Precise prompt

              Soft-delete projects: add a nullable deleted_at column, make DELETE /projects/:id set it to now(), hide rows with deleted_at from all normal queries, add POST /projects/:id/restore, and remove rows deleted more than 30 days ago in a nightly job.

              Typical resultDeleted projects leave every list but can be restored for 30 days; after that the nightly job removes them for good.

              Seen on

              • Laravel: Eloquent's SoftDeletes trait sets a deleted_at column instead of removing the row, leaves those rows out of queries, and restore() brings them back.

              You might describe it as

              • put it in the trash instead of deleting forever
              • let people undo a delete
              • keep the row but hide it

              Not to be confused with

              • Audit log

                A soft delete keeps the deleted row so it can come back; an audit log keeps a record of who deleted it and when.

              Upsert

              Also called: insert or update, ON CONFLICT DO UPDATE, merge.

              Save the same email twice. The upsert adds the row the first time and updates that row the second time. A plain INSERT fails on the unique email instead.

              2 rows · 0 rows for ana@example.com

              ana@example.com is not in the table yet.

                Ready

                One database write that inserts a row if it isn't there yet, or updates it if it is, matched by a unique key. It avoids the race where two requests both check "not there" and both insert.

                Say it in a prompt

                Import the product CSV with an upsert keyed by sku, using INSERT ... ON CONFLICT (sku) DO UPDATE to add new SKUs and update price and stock for existing ones. Write in batches of 500 rows and report how many rows were inserted and how many were updated.

                Vague vs precise prompt

                Vague prompt

                save the user's settings

                Typical resultReads the row, then inserts or updates in two separate steps. Two tabs saving at the same moment can create two settings rows, or one of them fails on a duplicate key.

                Precise prompt

                Save settings with an upsert on user_settings keyed by user_id: INSERT ... ON CONFLICT (user_id) DO UPDATE SET theme = EXCLUDED.theme, updated_at = now(), and keep the unique index on user_id.

                Typical resultThere is always exactly one settings row per user, and two saves at the same moment simply keep the later change.

                Seen on

                • PostgreSQL: INSERT ... ON CONFLICT DO UPDATE inserts or updates in one atomic step; the docs note it is also known as UPSERT.
                • Laravel: Eloquent's upsert method inserts or updates many records in one atomic operation, matched by the columns you name in uniqueBy.

                You might describe it as

                • create it if it's new, update it if it's already there
                • save without getting a duplicate key error
                • add or replace the row in one step

                Not to be confused with

                • Idempotent request

                  An upsert makes one database write safe to repeat; an idempotent request makes a whole API call safe to repeat.

                Webhook

                Also called: HTTP callback, event notification, webhook endpoint.

                When a payment succeeds, Stripe calls your server; your server never asks. If your server is down, Stripe tries again later, waiting longer each time (sped up here). Any 2xx answer counts as delivered: 200 here, 202 in the GitHub prompt.

                Tries: 0 · Orders paid: 0 · Your server asked Stripe: 0 times

                No payment yet. Your server is waiting; it does not ask Stripe for news.

                  No events

                  Another service calls a URL on your server when something happens there, like "payment succeeded". You don't have to keep asking; the event comes to you.

                  Say it in a prompt

                  Receive GitHub push events at POST /webhooks/github. Check the X-Hub-Signature-256 HMAC against our secret, answer 202 within 10 seconds and queue a build job for the pushed branch, and skip any delivery whose X-GitHub-Delivery id we have already handled.

                  Vague vs precise prompt

                  Vague prompt

                  let my app know when a Stripe payment goes through

                  Typical resultChecks the Stripe API every minute from a cron job, or accepts any JSON posted to /stripe without checking who sent it. When Stripe sends the same event again, the order is handled twice.

                  Precise prompt

                  Add POST /webhooks/stripe: verify the Stripe-Signature header with the endpoint secret using the raw body, reply 200 at once and do the work in a background job, skip event ids already processed, and on payment_intent.succeeded mark the order paid.

                  Typical resultOrders turn paid a few seconds after payment, fake requests get 400, and a resent event never marks an order paid twice.

                  Seen on

                  • GitHub: Webhooks deliver data to your server whenever an event you subscribed to happens; GitHub says they need fewer resources than polling the API.
                  • Stripe: Each event is signed in a Stripe-Signature header, and failed deliveries are retried for up to three days with exponential backoff.

                  You might describe it as

                  • the other service calls my server when something happens
                  • get told when a payment goes through
                  • a URL that another app sends events to

                  Not to be confused with

                  • Polling

                    A webhook is the other service calling you once when something happens; polling is you asking again and again.