[gtld-tech] CZDS system updates for access extension requests

Viktor Dukhovni ietf-dane at dukhovni.org
Thu Jun 30 08:22:02 UTC 2022

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

    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.


More information about the gtld-tech mailing list