Interview API design
0-day streakReview

Your progress

Level 1

0 XP100 XP to level 2
0day streak
0best streak
0topics started
0cards reviewed
    Review cards

    Progress is stored only in this browser. No account is required.

    Layer 4 · Full breakdown · 19 min · 5 figures

    API design for system design interviews

    How to do the API step of a design interview in about five minutes: map each requirement to an endpoint, pick a style, add the scale details (identity, pagination, idempotency, async work) and answer the follow-ups.

    The API step is the few minutes of a system design interview where you write down the contract between your clients and your system: one endpoint per requirement, the fields that matter in each request and response, and the handful of details that decide whether the design survives real traffic. Done well, it proves you covered every requirement, gives your architecture diagram its request paths, and plants the hooks (idempotency keys, cursors, 202s) that your deep dives will use later. This page is a recipe for doing it in about five minutes, plus the follow-ups that come next.

    Picture it first: the reservation slip

    Before online booking, a train ticket started with a paper requisition slip at the station counter. That slip is a well-designed API.

    • It asks only for what the clerk needs: train number, date, class, stations, and each passenger’s name and age. It never asks which ledger page or quota the berth comes from. That’s the clerk’s business.
    • Your identity is checked at the window, against the ID card in your hand, not taken from whatever name you wrote at the top.
    • You get back a PNR, a short number that means nothing to you but names your booking. Every later question, “is it confirmed?” or “cancel it”, starts with that number.
    • If the booking can’t be settled on the spot, say you’re waitlisted, you don’t stand at the window for a week. You leave and check the PNR status later.
    • A careful clerk handed the same slip twice, because you weren’t sure the first one went through, gives you the same PNR instead of a second berth.

    Map it back: the slip is the request body (only the fields that matter), the ID check is the auth token, the PNR is the resource ID returned with 201 Created, checking status later is 202 plus a status resource, and the careful clerk is an idempotency key. Each line of the slip takes seconds to fill in. That is the right pace for this step of the interview, too.

    Where the API step fits

    Most design rounds follow the same arc: clarify requirements, name the core entities, define the API, draw the high-level design, then go into deep dives. The delivery framework walks through the whole arc. The API sits in the middle for a reason: it turns requirements into concrete operations, and every operation becomes a request path you draw next.

    The API step in a typical round. Requirements and entities flow in; endpoints flow out, carrying details you will come back to later.Notice The API step is short but not shallow: each scale detail you name here becomes a ready-made deep dive.

    While you write endpoints, the interviewer is checking five things:

    • Coverage. Every functional requirement maps to at least one endpoint, and nothing out of scope sneaks in.
    • The client’s view. What does the app send, and what does it need back to draw the screen?
    • Security sense. Who is calling, and may they touch this object?
    • Scale awareness. Do lists page? Are writes safe to retry? Does slow work block a request?
    • Time discipline. Five minutes, not fifteen. Listing every CRUD route for every entity is the classic way to burn time here.

    The recipe, step by step

    We’ll run the recipe on an IRCTC-style tatkal booking system: a quota of berths opens at a fixed time, say 10:00 IST, and a crowd many times the size of the quota tries to book in the first few minutes.

    Step 1: list the functional requirements

    You wrote these already; read them back. Users should be able to:

    1. Check availability for a train, date and class.
    2. Hold berths for their passengers for a few minutes.
    3. Book and pay for the held berths.
    4. See their bookings.
    5. Cancel a booking.

    Step 2: name the resources

    Pull out the nouns: Train, Availability, Hold, Booking (with its passengers) and Payment. Notice what’s missing: User. The caller is whoever the token says, so the user rarely needs to appear as a field.

    API resources are not your tables. Google’s resource-oriented design guide (AIP-121) calls an API that is identical to its database schema an anti-pattern, because it couples every caller to your storage. A hold might live in Redis and a booking in PostgreSQL; the client never needs to know.

    Step 3: one endpoint per requirement

    Each requirement becomes a verb on a resource:

    GET  /v1/trains/{train_no}/availability?date=2026-11-02&class=3A
    POST /v1/holds
    POST /v1/bookings
    GET  /v1/bookings?limit=20&cursor=<opaque>
    POST /v1/bookings/{booking_id}:cancel

    Five requirements, five endpoints. If a requirement needs no endpoint, or an endpoint serves no requirement, say why.

    Requirements to endpoints for the tatkal-style booking system. Each row is one requirement, one verb on one resource, and the one detail that matters at scale.Notice User is not a resource in any path: it comes from the token, which is why “my bookings” is just GET /v1/bookings.

    Step 4: sketch the tricky requests and responses

    Don’t write out all five. Pick the one or two where the shape matters, and show only the fields that drive the design:

    POST /v1/holds
    Authorization: Bearer <token>
    
    { "train_no": "12951", "date": "2026-11-02", "class": "3A",
      "passengers": [{ "name": "Asha", "age": 34, "berth_pref": "lower" }] }
    
    201 Created
    Location: /v1/holds/hld_8Kq2
    { "hold_id": "hld_8Kq2", "berths": ["B4-23"],
      "fare": { "amount": 162050, "currency": "INR" },
      "expires_at": "2026-11-01T04:40:00Z" }

    Three small choices carry weight. The fare is an integer in paise (162050 is ₹1,620.50), so there is no floating-point rounding. The expiry is an RFC 3339 timestamp in UTC: 04:40Z is 10:10 IST, ten minutes after the window opened. And the response is the hold itself, so the app can show a countdown without a second call.

    POST /v1/bookings
    Authorization: Bearer <token>
    Idempotency-Key: 3f6c2a9e-7b1d-4e8a-9c55-0d2e8f4b7a13
    
    { "hold_id": "hld_8Kq2", "payment_method": "upi" }
    
    201 Created
    Location: /v1/bookings/bk_5Tz9
    { "booking_id": "bk_5Tz9", "status": "awaiting_payment",
      "payment": { "redirect_url": "<psp-url>", "expires_at": "2026-11-01T04:40:00Z" } }

    The booking exists at once, in an awaiting_payment state, because the money moves in the user’s UPI app, not inside this request.

    Step 5: add the scale details

    This is where strong answers pull ahead. Run down the list and say each one out loud:

    • Identity from the token. No user_id in the body or path. If the user must appear in a path, use an alias such as /v1/users/me, which Google’s naming guide (AIP-122) allows for the authenticated caller.
    • Pagination on every list. A limit with a documented default and maximum (say 20 and 100) plus an opaque cursor; the response carries next_cursor. Google’s pagination guide (AIP-158) requires paging from the first version, because adding it later breaks clients. See pagination.
    • An idempotency key on every non-idempotent write that matters. Here that is both POST /v1/holds and POST /v1/bookings: a retried hold could grab a second set of berths, and a retried booking could charge twice. See idempotency.
    • Async for slow or external work. Payment confirmation arrives later as a webhook from the payment gateway. Anything that takes more than a couple of seconds returns 202 with a status resource (next section).
    • A push channel only if needed. Booking status can be polled or sent as a mobile notification; a live seat map would justify SSE.
    • Rate limits. Per user and per IP, especially when bots join the 10 am rush. Over the limit: 429 with Retry-After. See rate limiting.

    In the interview

    One sentence buys a lot of credit: “The user comes from the auth token, never the request body, and every endpoint checks that the caller owns the object it names.” It shows you know the most common API vulnerability before anyone asks.

    Put together, the booking flow looks like this. The interesting moment is step 2, where the response is lost and the retry must not book twice.

    Hold, book, pay, confirm. The booking API holds a berth, creates the booking, survives a lost response, and learns about the payment from the gateway’s webhook.Notice The retry in step 3 returns the same booking: the Idempotency-Key turns “did my request land?” into a question with a safe answer.

    Step 6: errors in one line

    Don’t list every code. One line covers it: 400 invalid input, 401 no valid token, 404 not found (also when the booking belongs to someone else, so you don’t confirm it exists), 409 berths gone or hold expired, 429 slow down, 5xx retry with backoff if the call is idempotent. Bodies use the standard problem-details format described in What is an API?.

    409 Conflict suits a sold-out hold: RFC 9110 defines it as a clash with the resource’s current state that the user might resolve and resubmit, for example by choosing another class.

    Step 7: name the protocol and why

    One sentence is enough: “REST and JSON over HTTPS for the apps, because every client and CDN speaks it; gRPC from the booking service to inventory, for typed, low-latency internal calls; the payment gateway calls us back with a webhook.”

    Slow work: 202, a status resource and a webhook

    Some work can’t finish while a client waits: a Flipkart-style seller importing a 50,000-row catalogue, a video transcode, a monthly GST report. Holding the request open is fragile. Gateways and mobile networks drop long connections, and a client that times out and retries starts the job twice.

    Instead, accept the job and hand back a place to watch it. RFC 9110 calls 202 Accepted intentionally noncommittal: the request was accepted but not completed, and the response ought to point to a status monitor. Microsoft’s Azure API guidelines spell the pattern out: return 202 with an Operation-Location header (plain Location works too), let clients poll a status resource whose status moves through values such as NotStarted, Running, Succeeded and Failed, send Retry-After while it is running, and keep the status for at least 24 hours after it finishes.

    POST /v1/catalog-imports        Idempotency-Key: <uuid>
    { "file_id": "upl_77a" }
    → 202 Accepted
      Location: /v1/import-jobs/imp_42
      { "id": "imp_42", "status": "queued" }
    
    GET /v1/import-jobs/imp_42
    → 200 OK   Retry-After: 10
      { "id": "imp_42", "status": "running", "progress": 0.4 }
    A 50,000-row catalogue import as an asynchronous job. The first call returns in milliseconds; the work happens on workers; the seller polls or gets a webhook.Notice No connection stays open while the workers run: the client holds a URL, not a socket.

    Polling is simple and works through any firewall; you pay with wasted calls and up to one polling interval of delay. A webhook tells the seller the moment the job is done, but the seller needs a public endpoint and must verify signatures. Offer both: the webhook for speed, the status URL as the source of truth. More in long-running jobs.

    Choosing the style

    You don’t need a debate here; you need a default and one sentence of reasoning. The full comparison lives in REST vs GraphQL vs gRPC.

    Situation Default Why What you pay
    Apps, browsers and partners calling you REST + JSON over HTTPS Every client, proxy and CDN understands it; GETs cache Several calls per screen; fixed response shapes
    Service-to-service calls at high rates gRPC Typed contract, compact binary messages, deadlines, streaming Browsers need a proxy; harder to read by eye
    Many clients wanting different slices of the same data GraphQL, or a backend-for-frontend One round trip, shaped by the client Harder HTTP caching; query cost limits; N+1 lookups
    Server must push updates to a user SSE one way, WebSocket both ways No polling delay Millions of open connections to run
    Telling another company something happened Webhooks The partner doesn’t poll Retries, signatures, out-of-order events
    Work that takes seconds to hours REST 202 + status URL (+ webhook) No connection held open Client must poll or receive a callback
    Picking an API style in an interview. Most designs use two or three of these at once.

    Conventions that read well on a whiteboard

    Interviewers don’t grade naming, but clean names read as experience, and messy ones (/getBookingsForUser) invite questions you don’t need. These conventions come mostly from Google’s API Improvement Proposals and Microsoft’s guidelines:

    Convention Example Why
    Plural nouns for collections /v1/bookings, /v1/bookings/bk_5Tz9 AIP-122 requires plural collection names; the HTTP method is the verb
    Nest one level, for real ownership /v1/trains/{train_no}/availability Deep paths get brittle; filter instead: /v1/bookings?train_no=12951
    Non-CRUD actions as custom methods POST /v1/bookings/{id}:cancel AIP-136: POST for side effects, verb after a colon; a /cancel sub-path also reads fine
    Reads never change state GET only for reads RFC 9110 defines GET as safe, so crawlers and prefetchers may call it freely
    Filters, sort and paging in the query ?status=confirmed&sort=-created_at&limit=20 One resource URL serves every view
    IDs as opaque strings "bk_5Tz9", not 1042 AIP-122: ID fields should be strings; unguessable IDs slow enumeration
    Timestamps in RFC 3339, UTC "2026-11-01T04:40:00Z" Microsoft’s guidelines require RFC 3339; UTC removes time-zone bugs
    Money as integer minor units plus currency { "amount": 162050, "currency": "INR" } 0.1 + 0.2 is not exactly 0.3 in floating point; paise are exact
    Enums as strings that may grow "status": "awaiting_payment" Readable in logs; Microsoft advises telling clients new values may appear
    Major version in the path /v1/... Breaking changes go to /v2; see What is an API?

    Don’t design the database in the API step

    Field lists of twenty columns, foreign keys and indexes belong to the data model, later. In the API step, show the two or three fields that change the design (hold_id, expires_at, next_cursor) and write “…” for the rest. The interviewer is checking that you think from the client inwards, not that you can type a schema.

    Worked examples

    Each example is the endpoint list you’d write, one sketch, and the choice an interviewer is likely to poke at.

    URL shortener

    POST /v1/links     { "long_url": "https://shop.example/sale?id=88", "alias": null }
    → 201 Created      { "code": "3cVrDte", "short_url": "https://sho.rt/3cVrDte" }
    GET  /{code}       → 302 Found   Location: <long_url>
    GET  /v1/links/{code}/stats?from=…&to=…

    The interesting choice is the redirect code. RFC 9110 marks 301 Moved Permanently as heuristically cacheable, so a browser may skip your server on the next click: less load, but those clicks vanish from analytics and you can’t retarget or block the link for that browser. 302 Found brings every click back to you, at the price of serving every redirect. Most designs choose 302 and absorb the load with caching, as the URL shortener design does. The redirect lives at the root, not under /v1, because short links must stay short.

    Ride hailing

    POST /v1/rides                    Idempotency-Key   { pickup, drop, product }
    → 201 { "ride_id": "rd_19", "status": "matching", "fare_estimate": {...} }
    GET  /v1/rides/{ride_id}/events   SSE stream: driver_assigned, location, arrived
    PUT  /v1/drivers/me/location      { "lat": 12.9716, "lng": 77.5946, "recorded_at": "…Z" }
    POST /v1/rides/{ride_id}:accept   (driver) → 200, or 409 if another driver won

    Driver location is the volume question. Assume 500,000 online drivers sending a fix every 4 seconds: 500,000 ÷ 4 = 125,000 writes per second. PUT fits because each fix replaces the last one rather than adding to a list, so a retried update is harmless, and recorded_at lets the server ignore a late, older fix. At that rate many designs move driver updates onto the WebSocket the driver app already holds, trading an HTTP request per fix, with its headers and token check, for one small message on an open connection. The rider watches a one-way SSE stream. :accept returns 409 when two drivers race for one ride, because only one conditional update can win. See Design Uber and real-time connections.

    News feed

    POST /v1/media-uploads    { "content_type": "image/jpeg", "size_bytes": 4000000 }
    → 201 { "media_id": "med_3x", "upload_url": "<pre-signed PUT URL>", "expires_at": "…Z" }
    PUT  <upload_url>         raw bytes, straight to object storage
    POST /v1/posts            Idempotency-Key   { "text": "…", "media_ids": ["med_3x"] }
    GET  /v1/feed?limit=20&cursor=<opaque>
    → 200 { "items": [...], "next_cursor": "…" }
    PUT    /v1/posts/{post_id}/likes/me
    DELETE /v1/posts/{post_id}/likes/me

    Three choices to defend. First, photo bytes never pass through your API servers. The API hands out a pre-signed URL, a short-lived link signed with your credentials that lets the app PUT one object straight into storage. Amazon’s S3 documentation allows up to 7 days of validity through the SDKs, but anyone holding the URL can use it, so keep it to minutes. Second, the feed pages with an opaque cursor, never an offset, because new posts arriving at the top would shift every page. Third, a like is a PUT on likes/me, so double-taps and retries leave exactly one like. Details in Design a news feed and large file uploads.

    Posting a photo with a pre-signed URL. The API handles small metadata calls; the bytes go straight from the phone to object storage.Notice The thick arrow never touches the Post API, so a burst of uploads doesn’t tie up API servers or their memory.

    Sending the photo inside the JSON is worse than it looks: base64 turns every 3 bytes into 4 characters, so a 4 MB photo becomes about 4 × 4 ÷ 3 ≈ 5.3 MB of request body that an API server must buffer, parse and forward.

    Weak and strong answers side by side

    Weak answer Strong answer
    Endpoint POST /createBooking POST /v1/bookings
    Identity userId in the body User from the bearer token; ownership checked per object
    Retries Not mentioned Idempotency-Key header; a retry replays the first result
    Lists GET /bookings returns everything limit (default 20, max 100) and an opaque cursor
    Success 200 {"ok": true} 201 Created, a Location header, the booking in the body
    Errors 200 with an error message inside The right status class plus a problem-details body; 409 when berths are gone
    Slow work Request held open for 30 seconds 202 with a status URL, plus a webhook
    Uploads Base64 photo inside the JSON Pre-signed URL straight to object storage
    Time 15 minutes on every CRUD route About 5 minutes, core requirements only
    The same API step, answered two ways.

    Going deeper: the follow-ups interviewers probe

    “Why not just send userId?” Because the client controls the body: anyone can change the ID and act as someone else. Take identity from the verified token, and on every request that names an object (GET /v1/bookings/bk_5Tz9), check that the caller may access that object. OWASP ranks this failure, Broken Object Level Authorization, first in its API Security Top 10 for 2023, and warns that merely comparing the token’s user ID with an ID parameter covers only a small subset of cases. Random IDs slow enumeration, but they are not a permission check.

    “What if a list has a million items?” Every list gets a limit and a cursor from day one. AIP-158 explains why it can’t wait: a client with 75 items that always received all 75 silently gets only the first page once paging appears. Cursors also stay fast deep into a list, where an offset makes the database read and throw away every skipped row.

    “The client timed out. Can it retry?” GET, PUT and DELETE are idempotent by RFC 9110’s definition, so yes. POST and PATCH are not, so the client sends an Idempotency-Key: a fresh UUID per operation, reused on every retry. The IETF draft standardising the header suggests 409 when a retry arrives while the first attempt is still running and 422 when a key is reused with a different body. Stripe’s v1 API, for example, takes keys up to 255 characters and may prune them once they are 24 hours old.

    “Users upload 50 MB videos. Through your API?” No. Big bodies tie up API connections and memory for the whole upload, and they scale with bytes, not requests. Hand out a pre-signed URL, let object storage take the bytes, and keep the API for metadata.

    “What if the request takes 30 seconds?” Return 202 Accepted with a status resource, do the work from a queue, and notify by webhook or let the client poll with Retry-After. Never make a client hold a connection open for slow work.

    “The home screen needs five calls. Isn’t that slow on mobile?” Yes. Assume 100 ms per round trip on a mobile network: five sequential calls cost 5 × 100 = 500 ms before anything renders, while one aggregate call costs about 100 ms plus a few milliseconds of fan-out inside the data centre. Add a backend-for-frontend endpoint such as GET /v1/home, or use GraphQL. See API composition and chatty I/O.

    “PUT, PATCH or POST?” POST creates in a collection when the server picks the ID, or runs an action. PUT replaces the whole resource at a known URL, so repeating it is harmless, which suits “set driver location”. PATCH changes some fields: a patch that sets values is safe to repeat in practice, one that increments is not. A state change with rules and side effects, like cancelling with a refund, reads better as an action (:cancel) than as PATCH {"status": "cancelled"}.

    “How do you version it?” /v1 in the path from day one. Add optional fields freely; anything that removes, renames or changes meaning goes to /v2, and the old version stays up while clients move. The full story is in What is an API?.

    “The app shows ‘not found’ right after creating a booking.” The follow-up read hit a lagging replica. Return the created resource in the 201 body so the client needs no follow-up read, and send a user’s reads of their own fresh writes to the primary. AIP-121 makes this a rule: after a successful create, a get must return the resource.

    “What stops one client hammering you?” Rate limits per user, API key and IP at the gateway, answering 429 Too Many Requests with Retry-After, which RFC 9110 allows as seconds or a date. See rate limiting.

    Cheat sheet

    The API step on one whiteboard:

    API: REST + JSON over HTTPS, /v1, Authorization: Bearer <token>   (user = token)
    FR1  GET   /v1/things?filter=…&limit=20&cursor=…   → 200 { items, next_cursor }
    FR2  POST  /v1/things        + Idempotency-Key     → 201 + Location + thing
    FR3  GET   /v1/things/{id}                          → 200 | 404
    FR4  POST  /v1/things/{id}:verb                     → 200 | 409
    FR5  POST  /v1/jobs   (slow)                        → 202 + Location: /v1/jobs/{id}
    Push:     GET /v1/things/{id}/events (SSE) or a WebSocket, only if live
    Partners: webhooks, signed and retried
    Errors:   400 · 401 · 404 · 409 · 429 + Retry-After · 5xx   (problem+json)
    Internal: gRPC between services
    Check Default answer
    Identity From the token; /users/me alias; object-level check on every request
    Lists limit (default 20, max 100), opaque cursor, next_cursor
    Writes that cost money or stock Idempotency-Key; replay the first result
    Creates 201 + Location + the resource
    Slow work 202 + status URL + Retry-After; webhook when done
    Big files Pre-signed URL to object storage
    Live updates SSE one way, WebSocket both ways
    Abuse 429 + Retry-After
    Formats String IDs, RFC 3339 UTC, money in minor units, string enums

    A time budget, not a rule

    These numbers are a rule of thumb, not a standard: about five minutes for the whole step. Roughly one minute to list endpoints, two to sketch the one or two tricky requests, one for the scale details, and thirty seconds each for errors and protocol. If the interviewer waves you on early, move on; if they push on one endpoint, a deep dive has started, so follow it.

    How to say it in the interview

    “For the API, I’ll map each requirement to one endpoint, REST and JSON for the apps. Availability is GET /v1/trains/{train_no}/availability, served from cache. Holding berths is POST /v1/holds, which returns a hold ID and an expiry ten minutes out, or 409 if the berths are gone. Booking is POST /v1/bookings with the hold ID and an Idempotency-Key header, so a retry after a dropped connection can’t book or charge twice; it returns 201 with the booking in awaiting_payment, and the payment gateway confirms by webhook. ‘My bookings’ is a cursor-paginated GET, and cancel is POST /v1/bookings/{id}:cancel. The user always comes from the token. Internally, booking talks to inventory over gRPC.”

    Why not pass the user ID in the request? The client controls the body, so anyone could book as anyone. Identity comes from the verified token, and each endpoint checks that the caller owns the object it names; missing that check is OWASP’s top API risk.

    How do you make booking safe to retry? The client generates a UUID per booking attempt and sends it as Idempotency-Key on every retry. The server stores the key with the result in the same transaction and replays the first response; a retry that arrives mid-flight gets 409.

    What if the request takes 30 seconds? Then it shouldn’t be a blocking call. Return 202 Accepted with a status URL, run the work on a worker, and let the client poll with Retry-After or receive a webhook.

    How do you paginate a feed? With limit and an opaque cursor, never an offset, because new posts at the top would shift offsets and repeat items. For a ranked feed, the cursor points into a snapshot ranked once per session.

    REST or GraphQL here? REST: there are few client types, the reads cache well and the tooling is simple. I’d switch to GraphQL or a backend-for-frontend if web, Android and partner apps each needed different shapes of the same data.

    Where to go next

    References and further reading

    1. RFC 9110 — HTTP Semanticsrfc-editor.orgSafe and idempotent methods, POST and PUT semantics, 201 with Location, the noncommittal 202, 409 Conflict, 301 vs 302 caching and the Retry-After header.
    2. Google AIP-121 — Resource-oriented designgoogle.aip.devNouns as resources and a few standard verbs, why an API that mirrors the database schema is an anti-pattern, and read-after-create consistency.
    3. Google AIP-122 — Resource namesgoogle.aip.devPlural collection identifiers, string IDs, and aliases such as users/me for the authenticated caller.
    4. Google AIP-136 — Custom methodsgoogle.aip.devActions that don’t fit create, get, update or delete: POST for side effects and a colon-separated verb such as :archive.
    5. Google AIP-158 — Paginationgoogle.aip.devPaginate every list from the first version, opaque page tokens that grant no access, and why adding paging later breaks clients.
    6. Stripe API reference — Idempotent requestsdocs.stripe.comClient-generated keys (V4 UUIDs suggested) up to 255 characters, replayed responses, and pruning after at least 24 hours in API v1.
    7. The Idempotency-Key HTTP Header Field (IETF draft-ietf-httpapi-idempotency-key-header-08)datatracker.ietf.orgThe header being standardised for POST and PATCH, with 400 for a missing key, 409 for a concurrent retry and 422 for a reused key.
    8. Amazon S3 User Guide — Download and upload objects with presigned URLsdocs.aws.amazon.comTime-limited upload URLs that need no AWS credentials, up to 7 days of validity through the SDKs, and why they must be guarded like bearer tokens.
    9. OWASP API Security Top 10 (2023) — API1: Broken Object Level Authorizationowasp.orgThe top API risk: callers changing object IDs to reach other users’ data, and why checking the token’s user ID alone is not enough.
    10. Microsoft Azure REST API Guidelinesgithub.com202 plus a status monitor for long-running operations, Retry-After while polling, RFC 3339 timestamps and extensible string enums.