AI & Automation

APIs, Webhooks and JSON for Non-Developers

A practical guide to APIs, webhooks and JSON for automation builders: requests, status codes, auth, rate limits, retries, signatures and reading nested data.

Mauricio Esparza By ·Published ·10 min read
mithub.club
Short answer

An API is how you ask another system to do something and get an answer back. A webhook is that system telling you the moment something happened, without being asked. JSON is the text format both use to write the message. Learn requests, status codes, authentication, rate limits, retries and nested data and you can debug most automations.

This goes deeper than the overview in what is AI automation. No coding required, but you should be willing to look at a payload.

In short

  • API = pull (you ask). Webhook = push (they tell you). JSON = the wording.
  • Status codes tell you whose fault it is. Learn six of them and you can debug alone.
  • Webhooks can arrive twice, late or out of order. Design for that from day one.
  • If a webhook triggers anything meaningful, verify that it really came from who you think.
  • Three kinds of empty — missing key, null, empty string — are three different bugs.

An API request has four parts

Every request, in any tool, is the same four things.

PartPlain meaningExample
MethodThe verb: what do you want done?GET, POST, PATCH, DELETE
URLThe address of the thinghttps://api.example.com/v1/contacts/482
HeadersMetadata about the request, including who you areAuthorization: Bearer abc123
BodyThe data you're sending, usually JSON{"stage": "Qualified"}

The methods you will actually use: GET reads and changes nothing, so it is safe to repeat. POST creates or triggers, and repeating it usually creates a duplicate. PUT typically replaces a whole record while PATCH changes only the fields you send — sending PUT when you meant PATCH is a classic way to wipe fields you never touched. DELETE is exactly as permanent as it sounds.

That safe/unsafe split matters for retries: re-running a GET is free, re-running a POST after a timeout may create a second deal.

Status codes: whose fault is it?

MDN groups status codes into five classes: informational (100–199), successful (200–299), redirection (300–399), client errors (400–499) and server errors (500–599) (MDN). The practical read: 4xx is your fault, 5xx is theirs.

CodeMDN's meaningWhat it means for you
200 OKThe request succeededDone. Check the body anyway — success can still return zero results
201 CreatedSucceeded and a new resource was createdYour record exists. Save the returned ID
400 Bad RequestThe server can't process it, perceived as a client errorYour JSON or field values are wrong. Read the error body
401 UnauthorizedThe client must authenticate itselfMissing, wrong or expired credentials
403 ForbiddenThe client doesn't have access rightsCredentials work; this account isn't allowed to do this
404 Not FoundThe server can't find the requested resourceWrong URL, or the record ID doesn't exist
429 Too Many RequestsToo many requests in a given amount of timeSlow down. See below
500 Internal Server ErrorThe server hit a situation it can't handleNot your bug. Retry later, and alert someone

The 401 versus 403 distinction saves hours: 401 means "I don't know who you are," 403 means "I know exactly who you are, and no." One is a credentials problem, the other a permissions problem, and they have completely different fixes.

Rate limits and how to back off

MDN describes 429 Too Many Requests as indicating the client "has sent too many requests in a given amount of time," a mechanism commonly called rate limiting, and notes that a Retry-After header may be included "to indicate how long a client should wait before making the request again" (MDN).

So the correct behaviour on a 429 is specific: read Retry-After if present and wait exactly that long rather than guessing; otherwise back off exponentially (1s, 2s, 4s, 8s, with a cap); limit the number of retries and send the item to an error queue instead of looping forever; and only retry safe operations automatically, because a retried POST duplicates records unless the API supports an idempotency key.

Batch jobs are where this bites. Enriching 5,000 records in a loop with no delay will hit a limit, and a workflow that treats 429 as a hard failure silently skips a thousand rows — see error handling for production workflows.

Authentication, briefly

Four patterns cover almost everything: an API key in a header such as X-API-Key: abc123; a bearer token as Authorization: Bearer abc123, the most common; basic auth with an encoded username and password, still common in internal tools; and OAuth, where you click "Connect" and the tool stores a token that refreshes — best for user-permissioned access, and expiring tokens are a real failure mode.

Two rules, no exceptions. Never put a key in a URL — URLs land in logs, browser history and error reports. And use the tool's credentials store rather than pasting keys into node fields, so you can rotate a key without editing twenty workflows.

Pagination: why you only got 100 records

Most APIs refuse to return everything at once. They hand you a page plus a way to ask for the next one — a page number, an offset, or a cursor token in the response. The symptom of getting this wrong is distinctive: your sync works perfectly and quietly handles exactly 100 records, forever. Before trusting any "get all" step, compare against the total count in the source system.

Webhooks: the part everyone underestimates

A webhook is a URL you give to another system so it can POST data to you when something happens. No polling, no delay. That's the easy part. The hard part is that webhook delivery is not as reliable as people assume, and well-written provider documentation says so out loud.

Stripe's webhook documentation is a good reference because it is explicit about delivery behaviour (Stripe):

  • Retries. Stripe attempts delivery for up to three days with exponential back-off in live mode when your endpoint doesn't succeed.
  • Duplicates. Endpoints "might occasionally receive the same event more than once." The defence: log the event IDs you've processed and skip ones already handled.
  • Order. Stripe doesn't guarantee events arrive in the order they were generated, and warns against using the created timestamp to determine order or detect duplicates — use event IDs.
  • Respond fast. Your endpoint must return a 2xx before any complex logic that could time out. Do the slow work after acknowledging.
  • Redirects count as failures. A 3xx response is treated as a failure; point the sender at the final URL.
  • Process asynchronously. Queue events rather than handling them synchronously, because spikes overwhelm a synchronous endpoint.

Those behaviours are typical of serious webhook providers, not unique to Stripe. Three habits prevent most webhook incidents: acknowledge first and work second (return 200, then process); make handling idempotent by checking whether you already processed this event ID, so "create a lead if one doesn't already exist" survives duplicates where "create a lead" does not; and never assume order — re-fetch current state from the API when sequence matters.

Verifying that a webhook is real

Your webhook URL is a public address. Anyone who learns it can post to it, and if that triggers a CRM write or an email, you have a problem.

Stripe signs every event with a Stripe-Signature header holding a timestamp and one or more signatures, computed as an HMAC with SHA-256 over the timestamp plus the raw request body and checked against an endpoint signing secret. Its libraries apply a default five-minute tolerance on that timestamp to limit replay attacks, and the docs also recommend IP allowlisting as a second protection (Stripe).

In no-code terms: use the provider's verification step if there is one; otherwise require a shared secret in a header and reject anything without it. An unauthenticated webhook that only writes to a log is fine. An unauthenticated webhook that sends messages is not.

Test URL vs production URL

A classic beginner trap with an exact explanation. n8n's Webhook node documentation describes two URLs: the test URL, which shows incoming data in the editor when you click Listen for Test Event, and the production URL, which registers when you publish the workflow and whose runs appear in the Executions tab instead. The node accepts DELETE, GET, HEAD, PATCH, POST and PUT, offers Basic auth, Header auth, JWT auth or none, supports an IP allowlist, and can respond immediately, when the last node finishes, via a Respond to Webhook node, or as a stream (n8n).

"It worked while I was building it and stopped afterwards" is almost always a test URL left in production. More on this in n8n webhooks explained and the broader tool in what is n8n.

Reading JSON without fear

MDN defines JSON as "a standard text-based format for representing structured data based on JavaScript object syntax," and notes it can hold strings, numbers, true, false, null, objects and arrays — but not functions, dates as real date objects, undefined, NaN or Infinity (MDN).

{
  "id": "L-1042",
  "created_at": "2026-09-17T14:02:00Z",
  "score": 82,
  "qualified": true,
  "owner": null,
  "company": {
    "name": "Northside Lending",
    "branches": 12
  },
  "tags": ["inbound", "webinar"],
  "contacts": [
    { "name": "Ana Torres", "role": "COO", "email": "ana@example.com" },
    { "name": "Luis Peña", "role": "Ops Manager", "email": "" }
  ]
}

Four things to notice, because they cause most real bugs:

1. Curly braces vs square brackets. {} is an object — named fields. [] is an array — an ordered list. An array of one item is still an array, and a step expecting an object will choke on it.

2. Paths. You reach a value by walking the structure: company.name is "Northside Lending", contacts[0].email is Ana's address, tags[1] is "webinar". Arrays start at 0 — the most common off-by-one in no-code automation.

3. The three kinds of empty. These are not the same thing, and confusing them creates bugs that only appear on certain records:

FormMeaningTypical cause
Key missing entirelyThe system never had this concept for this recordDifferent object type, or an older API version
"owner": nullThe field exists and is deliberately emptyUnassigned
"email": ""The field exists and holds an empty stringSomeone saved a blank form field

A filter that checks "email is not empty" but not "email exists" will happily pass records straight into a send step.

4. Types are not decoration. 82 is a number, "82" is a string; true is a boolean, "true" is a string. Comparisons and maths fail silently across that line. Dates are just strings — insist on the ISO 8601 form above (2026-09-17T14:02:00Z, where Z means UTC) whenever you control the format.

The MitHub integration contract

Fill this in before connecting any two systems. Ten minutes here saves a week of mystery.

FieldWhat to write
DirectionPull (API) or push (webhook)?
TriggerWhat event starts it, and what is the expected volume per hour?
IdentityAuth method, where the credential is stored, when it expires
Unique keyThe field that identifies a record in both systems
Required fieldsWhat must be present, or the record goes to the exception queue
Field mapSource field → destination field, with type and transformation
Duplicate ruleWhat happens when the same event arrives twice
Order ruleWhat breaks if events arrive out of order
LimitsRate limit and page size, and how you respect them
Failure pathRetry policy, error queue, who gets alerted
VerificationHow you confirm the business result in the destination system

The last row is the MitHub habit that matters most: a green run is not a result. Open the destination record and look. If that destination is your CRM, CRM architecture covers what "correct" means there.

A 20-minute drill

Do it once, today.

  1. Get a webhook URL from your automation tool and send it something from a test form. Watch the payload arrive.
  2. Read the JSON. Write down the path to three fields, including one inside an array.
  3. Break it on purpose. Send a payload with a field missing and another with an empty string. See what your steps do.
  4. Make one API call to a system you already use — a GET for a single record. Read the status code and the body.
  5. Break the auth. Remove a character from the key and confirm you get a 401, not a 500. Now you know what that looks like at 2am.

Deliberately causing five errors teaches more than ten successful runs — the practice loop behind how to learn AI automation.

Five mistakes to stop making

  1. Trusting a 200. It means the request was accepted, not that the business outcome is correct.
  2. Ignoring pagination. Compare totals with the source system.
  3. Retrying POST blindly. Duplicates are worse than failures, because nobody notices them.
  4. Leaving a webhook unverified. If it can act, it can be abused.
  5. Assuming the payload shape never changes. Validate required fields on arrival; route the rest to an exception queue.

None of this requires you to become a developer. It requires literacy in the three formats your systems already speak — the capability that moves you from doing tasks to designing systems. The free foundations of the Faculty of Revenue Reverse Engineering put that literacy to work on a real revenue process.

Frequently asked questions

What is the difference between an API and a webhook?

With an API you make the first move: you send a request and wait for a response. With a webhook the other system makes the first move: it sends data to a URL you gave it, the moment an event happens. APIs are pull, webhooks are push.

What does a 429 error mean?

MDN describes 429 Too Many Requests as the client having sent too many requests in a given amount of time, which is rate limiting. The response may include a Retry-After header saying how long to wait before trying again.

Can a webhook arrive twice?

Yes. Stripe's documentation states that webhook endpoints might occasionally receive the same event more than once, and recommends logging event IDs and skipping ones already processed. It also says event order is not guaranteed.

Do I need to verify webhooks?

If the webhook can trigger anything meaningful, yes. Anyone who learns the URL can post to it. Stripe signs every event with a Stripe-Signature header, using HMAC with SHA-256, and its libraries apply a default five-minute tolerance on the timestamp to limit replay attacks.

Sources

  1. Working with JSON — MDN Web Docs (accessed 2026-09-17)
  2. HTTP response status codes — MDN Web Docs (accessed 2026-09-17)
  3. 429 Too Many Requests — MDN Web Docs (accessed 2026-09-17)
  4. Receive Stripe events in your webhook endpoint — Stripe Docs (accessed 2026-09-17)
  5. Webhook node documentation — n8n Docs (accessed 2026-09-17)
AI AutomationAPIsWebhooksJSON
Mauricio Esparza
Mauricio EsparzaGTM Systems Lead · Revenue Engineer · Founder of MitHub. Designs and runs revenue systems for multi-location businesses: AI voice campaigns, enrichment, CRM automation and attribution. Founded MitHub to teach the method in the open.

Part of AI & Automation on MitHub.

Keep going