What 202 Accepted Actually Means for a Long-Running API
A 202 is as easy to return as a 200. What it commits you to is validating before you accept, publishing a way to track the work, and having an answer ready when the work finishes and nobody is listening.
Most .NET API developers return a 200 when an endpoint succeeds. It is easy, it
is one line, and most of the time it is correct. return Ok() and move on.
It stops being correct the moment the work happens somewhere else.
A 200 means the request completed. The caller sent something, your API did the work, and here is the result. That is an honest answer when it is true. When the request goes on a queue and a worker picks it up thirty seconds later, a 200 tells the caller the job is done. It isn’t. You have told them something false in a way they have no reason to question.
A 202 means the request was received and accepted, and the work is not finished yet. The caller knows to expect a delay, and knows not to read the response as the outcome.
That part is easy. return Accepted() is no harder to type than return Ok().
The responsibility that comes with it is the part people skip.
Validate before you accept
If you are going to process the request later, validate it now.
In a synchronous flow, a bad payload comes back as a 400 immediately and the caller fixes it. In an asynchronous flow with no up-front validation, that same bad payload gets a 202, fails in a worker an hour later, and the caller has long since moved on believing they sent something good.
Validate whatever you can check without doing the work: shape, required fields, whether the things being referenced actually exist, whether this caller is allowed to do this at all. A 202 is a promise that the request was good enough to act on. Do not make that promise casually.
Tell the caller how to follow it
Your API gets one response. The caller needs some way to find out what happened after that response is gone. There are three common approaches.
Location header
Location: /orders/123/status
The body stays clean and the mechanism is standard enough that most clients already know what to do with it.
Two things worth keeping straight. Location is formally specified for 201 and for redirects. Pointing it at a status resource on a 202 is well-established convention rather than a requirement, so it is worth documenting explicitly. And unlike a 201, where Location is the thing you created, on a 202 it is a status monitor. The finished resource may not exist yet.
Pair it with Retry-After so the caller knows how often to poll instead of
guessing. This gets left out constantly, and then the same team wonders why
their status endpoint is taking more traffic than the endpoint doing the work.
A payload in the body
Useful when the status URL is not the only thing you have to say. You can return an operation id, a cancel URL, an estimated completion, a position in the queue.
The cost is that you now own a contract. Everything in that body is something a client will take a dependency on, and changing it later is a versioning problem rather than an implementation detail. Worth it when you genuinely have more to communicate. Not worth it to avoid setting a header.
A caller-supplied callback
The caller gives you a URL, you call it when the work is done, and nobody polls anything. It is the cleanest version on paper.
In enterprise environments it is also the one most likely to quietly fail. Plenty of systems are allowed to make outbound calls and are not allowed to receive inbound ones. The callback goes nowhere. And once you own delivery, you own everything that comes with it: retries, backoff, what happens when the endpoint is down for six hours, and where a permanently undeliverable result goes so the completion is not simply lost.
Treat a callback as an optimization layered on top of a status endpoint, not as a replacement for one. Build the status endpoint regardless. Callers who can receive a push get their answer immediately. Callers who cannot still have a way to find out.
The point
The status code is the easy half. What a 202 actually commits you to is validating before you accept, publishing a way to track the work, and having an answer ready for the moment the work finishes and nobody is listening.