On Thu, Jun 30, 2022 at 01:49:10AM -0400, Viktor Dukhovni via gtld-tech wrote:
On Tue, Jun 28, 2022 at 06:05:45PM -0400, Viktor Dukhovni via gtld-tech wrote:
Is there an API mechanism to do the same? What are the URLs for requesting such an extension and what parameters do they take?
Can the API URLs and required parameters be directly inferred from the Web UI?
I was able to make partial progress, I can now retrieve the details for each approved request that will expire in less than 30 days, and see that the returned JSON data for each request id has among other fields:
{ ... , "extensible": true , "extensionInProcess": false , ... }
It now remains to "POST" a suitable HTTP request to the URL associated with the request in question. How is the URL in question formed from the request id, and what parameters, headers and request body does it expect?
With a bit more sleuthing, the API entry point is: fetch("https://czds-api.icann.org/czds/requests/extension/<requestId>", { "headers": { "content-type": "application/json", "authorization": "Bearer <auth-token>", "accept": "application/json", }, "body": "{}", "method": "POST" }); To get the request ids however one iterates fetch("https://czds-api.icann.org/czds/requests/all", { "headers": { "content-type": "application/json", "authorization": "Bearer <auth-token>", "accept": "application/json", }, "body": approved(n), "method": "POST" }); where approved(n : Int) is the encoding of JSON object: { "status": "Approved" , "filter": "" , "pagination": { "size": 100, "page": n } , "sort": { "field": "Expired", "direction": "asc" } } this returns an object with: { "requests": [ r1, r2, ..., ] , "totalRequests": N } where "totalRequests" is perhaps the total across all pages (which I ignore), and "requests" is a list of request summaries, which if empty signals that the page number is too high. However, in practice I stop well short of that, because once the request expiration time is more than ~29 days in the future, there no point in fetching the rest. The request objects look like: { ... -- stuff we don't need , "requestId": String -- the magic <requestId> , "expired": String -- ISO8601 expiration date-time (Zulu timezone) , "tld": String -- A-label TLD name , ... -- stuff we don't need } Often the first 100 is all you'll ever need, though perhaps now and then more than 100 will be up for expiration in 30 days or less. Armed with the "requestId", it is possible to grab the request details: fetch("https://czds-api.icann.org/czds/requests/<requestId>", { "headers": { "authorization": "Bearer <auth-token>", "accept": "application/json", }, "method": "GET" }); Which are another JSON object containing many fields, but you only need: "requestId": String "extensible": Bool "extensionInProcess": Bool to decide whether to request an extension for a request and has "extensible" true and "extensionInProcess" false. Now that my next 30 days of zones have all been requested for extension, I'm changing the code to only examine the details for requests that are at least 21 days from expiring and at most 29. With the script running every day, things that expire sooner will have been requested previously and don't need to be rechecked. Best of luck with your implementations. -- Viktor.