30

I have a REST endpoint accepting a POST request to mark a code as redeemed. The code can only be redeemed between certain dates.

How should I respond if someone attempts to redeem the code early?

I suspect HTTP 403, Forbidden, is the right choice but then the w3c states that "the request SHOULD NOT be repeated" whereas in this case I would anticipate the request being repeated, just at a later date.

Dan Midwood
  • 18,694
  • 7
  • 33
  • 32
  • Does the same URI respond to GET? Always or only between the certain dates? – fumanchu Jun 18 '12 at 15:16
  • Kind of. The Code representation is available to be GETted before it goes live. Redemptions are a seperate representation that can be created against the code. It is this POST request that creates the redemption. Clearly, a GET before creation will return a 404 because that redemption does not exist (note that this GET would use a redemption ID and not the code). – Dan Midwood Jun 18 '12 at 15:26
  • Does the POST create a resource at that URL? Another? None at all? – fumanchu Jun 18 '12 at 15:35
  • The POST is to redemption/ and creates a redemption resource that would then be available to GET on redemption/{ID}. The full URL (including ID) would be returned in the location header after a successful POST. Before the POST there is no redemption resource, so there is nothing to GET. – Dan Midwood Jun 18 '12 at 15:45
  • The heirarchy has a campaign (which has the start date), codes that are generated against the campaign and then (after the campaign has started) 1-n redemptions against each code. The Campaign and Codes are available before the start date, but redeeming a code is not allowed. – Dan Midwood Jun 18 '12 at 15:46
  • You should use the status code `202`, cf. [this answer](https://softwareengineering.stackexchange.com/a/316211/184279) for how. – Géry Ogam Jun 24 '21 at 00:19

4 Answers4

28

409 Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

403 Forbidden makes more sense if they are trying to redeem a coupon that has already been redeemed, though 410 Gone seams elegant in this situation as well.

404 Not Found isn't ideal because the resource does in fact exist, however you can use it if you don't want to specify a reason with the 403 or if you want to hide the existence of the resource for security reasons.

If you are using HATEOAS, then you can also head you clients off at the pass (so to speak) by only including a redeem hypermedia control in the coupon resource (retrieved via a GET) when the coupon can be redeemed; though this won't stop overly bound clients from trying to redeem it anyway.

Tom Howard
  • 6,516
  • 35
  • 58
  • Hi Tom, my first pass at this question made me lean towards 403. But after more thinking (and some pushback) I'm now leaning towards 409. The way you quote it, "due to a conflict with the current state of the resource" could be taken to mean that, at a later time, the request could be non-conflicting, and thus, valid. – David J. Jun 19 '12 at 06:17
  • @DavidJames that's exactly what convinced me as well. The coupon is in a non-redeemable state, so any attempts to redeem it would conflict with it's current state. – Tom Howard Jun 19 '12 at 10:04
  • Re "403 Forbidden makes more sense if they are trying to redeem a coupon that has already been redeemed", wouldn't 409 Conflict be the appropriate choice here too? – Dan Midwood Jun 19 '12 at 18:40
  • @DanMidwood from a resource state point of view, yes I would still stick to 409. However 409 "is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request." For a redeemed coupon, you coud argue that the conflict will never be resolved, so 409 shouldn't be used. That is why I suggested 403 and 410 for that situation. – Tom Howard Jun 19 '12 at 21:01
3

EDIT: Thanks to some good critiques (see below), I want to caveat this answer. It is based on Richardson & Ruby's writeup, which arguably doesn't mesh well with the httpbis writing on 403 Forbidden. (Personally, now I'm learning towards 409 as explained by Tom in a separate answer.)

403 Forbidden is the best choice. I will cite RESTful Web Services by Richardson & Ruby line by line. As you will see, 403 is a great fit:

The client's request is formed correctly, but the server doesn't want to carry it out.

Check!

This is not merely the case of insufficient credentials: that would be a 401 ("Unauthorized"). This is more like a resource that is only accessible at certain times, or from certain IP addresses.

Check!

A response of 403 implies that the client requested a resource that really exists. As with with 401 ("Unauthorized"), if the server doesn't want to give out even this information, it can lie and send a 404 ("Not Found") instead.

You wrote above: "The Code representation is available to be GETted before it goes live." So, you aren't trying to hide anything. So, stick with the 403. Check!

If the client's request is well-formed, why is this status code in the 4xx series (client-side error) instead of the 5xx series (server-side error)? Because the serve made it decision based on some aspect of the request other than its form; say, the time of day the request was made.

Check! The client's request was formed corrected, but it was inappropriate for the particular time.

We went four for four. The 403 code is a winner. No other codes match as well.

All of this said, a plain, non-specific 400 wouldn't be wrong, but would not be as specific or useful.

Another answer suggested the 409 Conflict code. Although worth considering, it isn't as good a fit. Here is why. According to Richardson & Ruby again:

Getting this [409] response response means that you tried to put the server's resources into an impossible or inconsistent state. Amazon S3 gives this response code when you try to delete a bucket that is not empty.

Claiming a promotion before it is 'active' wouldn't "put a server resource into an inconsistent state." It would break some business rules -- and result in cheating -- but it wouldn't cause a logical contradiction that I see.

So, whether you realized it at the onset of asking your question or not, 403 is a great choice. :)

Community
  • 1
  • 1
David J.
  • 31,569
  • 22
  • 122
  • 174
  • Richardson & Ruby didn't have the benefit of httpbis, apparently, which now says, "The server understood the request, but refuses to authorize it. Providing different user authentication credentials might be successful, but any credentials that were provided in the request are insufficient. The request SHOULD NOT be repeated with the same credentials." See http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-19#section-7.4.3 – fumanchu Jun 19 '12 at 05:45
  • @fumanchu You make a good point! The "don't repeat the request" has a certain finality to it. (It seems as if R&R may have stuck their necks out a little beyond the spec?) – David J. Jun 19 '12 at 06:14
1

Since Rest URLs should represent resources I would reply with 404 - Not Found
The resource is only available between certain dates, so on any other date it is not found.

VolkerK
  • 1,412
  • 13
  • 28
  • 1
    This is close but not the best choice. According to "RESTful Web Services", "a 404 may be a lie to cover up a 403 or 401. It might be that the resource exists, but the server doesn't want the client to know about it." Note that Dan, above, said that he isn't hiding the resource. He simply wants to restrict a certain operation on it (redeeming the code) to a certain time window. So we don't need to use the 404 as a cover up. – David J. Jun 19 '12 at 04:23
-1

When it says the request "SHOULD NOT be repeated", it is referring to the message that you should send to the viewer.

It has nothing to do with whether an actual request is repeated. (The user will get the same 403 message over and over again if s/he so desires.)

That said, a 404 is not appropriate for this because the resource is available - just that the code is not redeemable/forbidden to redeem. It is actually harmful because it tells the user that you probably made a mistake in your URL link or server configuration.

Of course, this assumes that on the appropriate date you return a 200 instead.

xiankai
  • 2,773
  • 3
  • 25
  • 31
  • Downvoter, can you explain? This seems like a reasonable answer to me – Dan Midwood Jun 18 '12 at 11:31
  • 2
    I'm not the downvoter, but my understanding is that the request *as is* (i.e. the parameters and headers the UA is sending) should not be repeated; that is, no matter how many times the exact same request is sent, it'll never get anything other than a 403. "*That request* is forbidden; please try another one"—sort of thing. – Asherah Jun 19 '12 at 03:13
  • This "answer" isn't really an answer and it wanders around without being clear. The first two sentences should really be comments on the question. Moreover, it doesn't really offer an answer to the question. So I think the downvote is deserved, and I'm adding another. – David J. Jun 19 '12 at 04:03
  • @Len: yes. However, there appears to be some tension between [W3C 403 Page](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) and what [Richardson & Ruby](http://shop.oreilly.com/product/9780596529260.do) write about the 403: "This is more like a resource that is only accessible at certain times, or from certain IP addresses." With this in mind, a client could issue the same exact request later and get a successful (2xx) result. – David J. Jun 19 '12 at 04:12
  • You're right, this were meant more as comments rather than a direct answer. However I don't have the reputation to comment yet, so I had to put them in an answer instead. I don't see why you would downvote if it offers relevant information but isn't a wrong answer? – xiankai Jun 19 '12 at 04:16
  • @xiankai: in general, the accepted practice is that only answers should be "answers", and everything else comments. Unfortunately that means newer users often get downvotes because they can't comment yet. :( In this case it's better to wait until you have the comment privilege. – Asherah Jun 19 '12 at 05:00