# Breaking changes in Asynchronous Order Finalization API

**URL:** <https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882>\
**Category:** Client dev\
**Created:** [April 5, 2023, 12:32pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882 "2023-04-05T12:32:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ppekrol](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/ppekrol/32/69208_2.png) [@ppekrol](https://community.letsencrypt.org/u/ppekrol)\
**Post date:** [April 5, 2023, 12:32pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/1 "2023-04-05T12:32:33Z")

</div>

Hi there,

we have noticed that on Staging, after recent changes that enable Asynchronous Order Finalization ([Enabling Asynchronous Order Finalization](https://community.letsencrypt.org/t/enabling-asynchronous-order-finalization/193522)) our client stopped working, because /order endpoint no longer returns in Location itself.

Example:

New API:

1. finalize: [https://acme-staging-v02.api.letsencrypt.org/acme/finalize/123/123](https://acme-staging-v02.api.letsencrypt.org/acme/finalize/123/123)
2. finalize response location: [https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123](https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123)
3. order response location: null

Old API:

1. finalize: [https://acme-staging-v02.api.letsencrypt.org/acme/finalize/123/123](https://acme-staging-v02.api.letsencrypt.org/acme/finalize/123/123)
2. finalize response location: [https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123](https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123)
3. order response location: [https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123](https://acme-staging-v02.api.letsencrypt.org/acme/order/123/123) (self)

Our code was getting the 'order' location and checking status in following manner (more or less):

var finalizeResponse = DoFinalize();  
var orderLocation = finalizeResponse.Location;  
while (true)  
{  
var statusResponse = CheckStatus(orderLocation);  
orderLocation = statusResponse.Location; // this is null after deployment of new API

```
if (statusResponse.Status == "valid")
{
    break;
}

```

}

Is there a chance that you could set the location to keep the backward compatibility with previous API?

---

<div class="post-metadata">

**Author:** ![orangepizza](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/orangepizza/32/19597_2.png) [@orangepizza](https://community.letsencrypt.org/u/orangepizza)\
**Post date:** [April 5, 2023, 1:36pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/2 "2023-04-05T13:36:05Z")

</div>

[@lestaff](https://community.letsencrypt.org/groups/lestaff) is this change intended?

---

<div class="post-metadata">

**Author:** ![aarongable](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/aarongable/32/42043_2.png) [@aarongable](https://community.letsencrypt.org/u/aarongable)\
**Post date:** [April 5, 2023, 2:31pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/3 "2023-04-05T14:31:18Z")

</div>

Hi there, thanks for the report! This exactly the kind of bug that staging deployments are meant to uncover. I don't expect the change to have affected the Location header but I'll look into it today.

---

<div class="post-metadata">

**Author:** ![aarongable](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/aarongable/32/42043_2.png) [@aarongable](https://community.letsencrypt.org/u/aarongable)\
**Post date:** [April 5, 2023, 4:07pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/4 "2023-04-05T16:07:24Z")

</div>

It's worth noting that RFC 8555 [does not require](https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1) a Location header for Finalize and Poll Order requests, _only_ for New Order requests:

```plaintext
   The following table illustrates a typical sequence of requests
   required to establish a new account with the server, prove control of
   an identifier, issue a certificate, and fetch an updated certificate
   some time after issuance. The "->" is a mnemonic for a Location
   header field pointing to a created resource.

   +-------------------+--------------------------------+--------------+
   | Action | Request | Response |
   +-------------------+--------------------------------+--------------+
   | Get directory | GET directory | 200 |
   | | | |
   | Get nonce | HEAD newNonce | 200 |
   | | | |
   | Create account | POST newAccount | 201 -> |
   | | | account |
   | | | |
   | Submit order | POST newOrder | 201 -> order |
   | | | |
   | Fetch challenges | POST-as-GET order's | 200 |
   | | authorization urls | |
   | | | |
   | Respond to | POST authorization challenge | 200 |
   | challenges | urls | |
   | | | |
   | Poll for status | POST-as-GET order | 200 |
   | | | |
   | Finalize order | POST order's finalize url | 200 |
   | | | |
   | Poll for status | POST-as-GET order | 200 |
   | | | |
   | Download | POST-as-GET order's | 200 |
   | certificate | certificate url | |
   +-------------------+--------------------------------+--------------+

```

So in general it may be wise to update your client to not rely on it, as other ACME Servers may not provide it.

Based on my reading of the code, I don't think that our usage of the Location header has changed. I believe that, both before and after this change, we did the following:

1. creating a new order: return a Location header pointing to the newly created Order (because it's required by RFC 8555)
2. finalize an order: return a Location header pointing to the existing Order (because the example at the very end of [Section 7.4](https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4) has one)
3. polling an existing order: do not return a Location header (because there is neither a requirement for one nor an example containing one)

You can see this behavior in action by making a simple GET requests for an Order URL to the Prod API (like [https://acme-v02.api.letsencrypt.org/acme/order/123/123](https://acme-v02.api.letsencrypt.org/acme/order/123/123), but with real account and order ID numbers) and observing the headers; they do not currently contain the Location header and this change has not yet been deployed to Prod.

When we were doing fully-synchronous finalization, your client worked, because it would never actually need to poll the order. It only got the Order object when creating a new order and when finalizing the order, and both of those contained the Location header.

Now that finalization can by asynchronous, your code now has to actually poll the order object, and _that_ response does not contain a Location header.

I'm happy to update our code to return a Location header in our get-order responses, but you should definitely update your client to work with servers that do not do so.

---

<div class="post-metadata">

**Author:** ![jvanasco](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/jvanasco/32/55900_2.png) [@jvanasco](https://community.letsencrypt.org/u/jvanasco)\
**Post date:** [April 5, 2023, 4:41pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/5 "2023-04-05T16:41:25Z")

</div>

> [@aarongable](#):
>
> I'm happy to update our code to return a Location header in our get-order responses, but you should definitely update your client to work with servers that do not do so.

The client should also have a test suite that runs against the [Pebble](https://github.com/letsencrypt/pebble) server, which implements the RFC strictly, and also implements non-required RFC elements differently than Boulder. Pebble already required polling, so this particular issue would have surfaced before the API change. Running tests against Pebble is likely to surface similar RFC related issues before any changes to LetsEncrypt's API are made .

---

<div class="post-metadata">

**Author:** ![ppekrol](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/ppekrol/32/69208_2.png) [@ppekrol](https://community.letsencrypt.org/u/ppekrol)\
**Post date:** [April 5, 2023, 4:42pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/6 "2023-04-05T16:42:05Z")

</div>

Thanks for investigation. We already updated code to not rely on the Location, so future versions of our product should be fully compatible with upcoming API. But if possible we would love to maintain backward compatibility due to older software versions that are already deployed by our customers, so if you could add back the Location header to get-order then it would be a fantastic news for us.

---

<div class="post-metadata">

**Author:** ![ppekrol](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/ppekrol/32/69208_2.png) [@ppekrol](https://community.letsencrypt.org/u/ppekrol)\
**Post date:** [April 5, 2023, 4:45pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/7 "2023-04-05T16:45:40Z")

</div>

Thanks for the tip, we will definitely set it up.

---

<div class="post-metadata">

**Author:** ![Phil](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/phil/32/76801_2.png) [@Phil](https://community.letsencrypt.org/u/Phil)\
**Post date:** [April 5, 2023, 5:49pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/8 "2023-04-05T17:49:43Z")

</div>

Can you PM me the user agent used by your client please? I'd like to do some spelunking through our logs.

---

<div class="post-metadata">

**Author:** ![orangepizza](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/orangepizza/32/19597_2.png) [@orangepizza](https://community.letsencrypt.org/u/orangepizza)\
**Post date:** [April 5, 2023, 9:08pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/9 "2023-04-05T21:08:53Z")

</div>

> [@Plesk: Certificate location not specified in response](https://community.letsencrypt.org/t/plesk-certificate-location-not-specified-in-response/195913):
>
> P…

looks like plesk not like this change

---

<div class="post-metadata">

**Author:** ![aarongable](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/aarongable/32/42043_2.png) [@aarongable](https://community.letsencrypt.org/u/aarongable)\
**Post date:** [April 5, 2023, 9:29pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/10 "2023-04-05T21:29:33Z")

</div>

I do think that error is related to async finalize, but I don't think it's related to Location headers. We'll keep that discussion over there.

---

<div class="post-metadata">

**Author:** ![mcpherrinm](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/mcpherrinm/32/59604_2.png) [@mcpherrinm](https://community.letsencrypt.org/u/mcpherrinm)\
**Post date:** [April 5, 2023, 10:45pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/11 "2023-04-05T22:45:35Z")

</div>

> [@Enabling Asynchronous Order Finalization](https://community.letsencrypt.org/t/enabling-asynchronous-order-finalization/193522/7):
>
> T…

We've disabled the brownout early because of problems like this

---

<div class="post-metadata">

**Author:** ![paulogdm](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/paulogdm/32/72005_2.png) [@paulogdm](https://community.letsencrypt.org/u/paulogdm)\
**Post date:** [April 6, 2023, 12:16pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/12 "2023-04-06T12:16:27Z")

</div>

Would the LE staff be open to clearing certain rate limits due to the brownout?  
At Vercel, we are adjusting our code, but the rate limits are still there (understandably) and causing some issues with specific domains.

---

<div class="post-metadata">

**Author:** ![Osiris](https://avatars.discourse-cdn.com/v4/letter/o/839c29/32.png) [@Osiris](https://community.letsencrypt.org/u/Osiris)\
**Post date:** [April 6, 2023, 12:31pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/13 "2023-04-06T12:31:48Z")

</div>

~~Which rate limit exactly? It probably isn't the duplicate cert rate limit with a window of 7 weeks (as you wouldn't be able to get certs if you were having trouble), but most likely the new order rate limit with a window of just 3 hours.~~

~~Are those 3 hours really a problem now that the brownout period has been disabled 14 hours ago?~~  
Nevermind, see below.

---

<div class="post-metadata">

**Author:** ![orangepizza](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/orangepizza/32/19597_2.png) [@orangepizza](https://community.letsencrypt.org/u/orangepizza)\
**Post date:** [April 6, 2023, 12:34pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/14 "2023-04-06T12:34:42Z")

</div>

wouldn't asynchronous finalization still mean CA start signing when client called finalization? CA would still sign in background but client never ask finished certificate.

---

<div class="post-metadata">

**Author:** ![Osiris](https://avatars.discourse-cdn.com/v4/letter/o/839c29/32.png) [@Osiris](https://community.letsencrypt.org/u/Osiris)\
**Post date:** [April 6, 2023, 12:46pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/15 "2023-04-06T12:46:11Z")

</div>

Hmm, good point. You can probably hit the rate limit regardless of the cert was downloaded or not.

---

<div class="post-metadata">

**Author:** ![paulogdm](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/paulogdm/32/72005_2.png) [@paulogdm](https://community.letsencrypt.org/u/paulogdm)\
**Post date:** [April 6, 2023, 1:18pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/16 "2023-04-06T13:18:46Z")

</div>

We are hitting the following:

> Error creating new order :: too many certificates (5) already issued for this exact set of domains in the last 168 hours: , retry after 2023-04-07T06:29:03Z: see [Duplicate Certificate Limit - Let's Encrypt](https://letsencrypt.org/docs/duplicate-certificate-limit/)

Trying to assess what we can do in the meantime, but clearing those rate limits would be lovely 😄

---

<div class="post-metadata">

**Author:** ![rg305](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/rg305/32/91314_2.png) [@rg305](https://community.letsencrypt.org/u/rg305)\
**Post date:** [April 6, 2023, 1:27pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/17 "2023-04-06T13:27:35Z")

</div>

Can ZeroSSL take some of the load [for the next week]?

---

<div class="post-metadata">

**Author:** ![paulogdm](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/paulogdm/32/72005_2.png) [@paulogdm](https://community.letsencrypt.org/u/paulogdm)\
**Post date:** [April 6, 2023, 1:45pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/18 "2023-04-06T13:45:03Z")

</div>

I am checking them out. Ty!

---

<div class="post-metadata">

**Author:** ![aarongable](https://sea3.discourse-cdn.com/letsencrypt/user_avatar/community.letsencrypt.org/aarongable/32/42043_2.png) [@aarongable](https://community.letsencrypt.org/u/aarongable)\
**Post date:** [April 6, 2023, 3:29pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/19 "2023-04-06T15:29:46Z")

</div>

Since you hit the Duplicate Certificates Rate Limit, you only need to wait 24 hours before you can attempt to issue again.

Other alternatives include:

- Recover the Order ID from your client logs, make a GET request for that Order, and it will have a certificate url from which you can download the Certificate
- Search crt.sh for your hostnames, and download the issued-and-logged-to-CT certificates from there
- Issue slightly different certificates (e.g. including or excluding www. variants of the domain names)

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/letsencrypt/original/3X/c/a/ca6c06ea1ea201324bba7048c6841ce60236468d.png) [@system](https://community.letsencrypt.org/u/system)\
**Post date:** [May 6, 2023, 3:30pm UTC](https://community.letsencrypt.org/t/breaking-changes-in-asynchronous-order-finalization-api/195882/20 "2023-05-06T15:30:45Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
