.. _`manage-rrsets`: Retrieving and Creating DNS Records ----------------------------------- All DNS information is composed of so-called *Resource Record Sets* (*RRsets*). An RRset is the set of all Resource Records of a given record type for a given name. For example, the name ``example.com`` may have an RRset of type ``A``, denoting the set of IPv4 addresses associated with this name. In the traditional BIND zone file format, the RRset would be written as:: 3600 IN A 127.0.0.1 3600 IN A 127.0.0.2 ... where 3600 is the number of seconds for which DNS resolvers may cache the information before asking again (time-to-live, short: *TTL*). Each of these lines is a Resource Record, and together they form an RRset. The basic units accessible through the API are RRsets (not individual Resource Records). The TTL is a property of an RRset; and all records of an RRset therefore share the record type and also the TTL. RRsets are each represented by a JSON object. The object structure is detailed in the next section. The relevant endpoints all reside under ``/api/v1/domains/{name}/rrsets/``, where ``{name}`` is the name of a domain you own. When operating on domains that don't exist or you don't own, the API responds with a ``404 Not Found`` status code. For a quick overview of the available endpoints, methods, and operations, see :ref:`endpoint-reference`. All operations are subject to rate limiting. For details, see :ref:`rate-limits`. .. _`RRset object`: RRset Field Reference ~~~~~~~~~~~~~~~~~~~~~ A JSON object representing an RRset has the following structure:: { "created": "2019-09-18T16:32:16.510368Z", "domain": "example.com", "subname": "www", "name": "www.example.com.", "type": "A", "records": [ "127.0.0.1", "127.0.0.2" ], "ttl": 3600, "touched": "2020-04-06T09:24:09.987436Z" } Field details: ``created`` :Access mode: read-only Timestamp of RRset creation, in ISO 8601 format (e.g. ``2019-09-18T16:32:16.510368Z``). ``domain`` :Access mode: read-only Name of the zone to which the RRset belongs. Note that the zone name does not follow immediately from the RRset name. For example, the ``com`` zone contains an RRset of type ``NS`` for the name ``example.com.``, in order to set up the delegation to ``example.com``'s DNS operator. The DNS operator's nameserver again has a similar ``NS`` RRset which, this time however, belongs to the ``example.com`` zone. ``name`` :Access mode: read-only The full DNS name of the RRset. If ``subname`` is empty, this is equal to ``{name}.``, otherwise it is equal to ``{subname}.{name}.``. ``records`` :Access mode: read, write Array of record content strings. Please note that when a record value contains a domain name, it is in almost all cases required to add a final dot after the domain name. This applies, for example, to the ``CNAME``, ``MX``, and ``SRV`` record types. A typical ``MX`` value would thus be be ``10 mx.example.com.`` (note the trailing dot). Please also consider the `caveat on the priority field`_. The maximum number of array elements is 4091, and the maximum length of the array is 64,000 (after JSON encoding). Records must be given in presentation format (a.k.a. "BIND" or zone file format). Record values that are not given in canonical form, such as ``0:0000::1`` for an IPv6 address, will be converted by the API into canonical form (here: ``::1``). Exact validation and canonicalization depend on the record type. ``subname`` :Access mode: read, write-once (upon RRset creation) Subdomain string which, together with ``domain``, defines the RRset name. Typical examples are ``www`` or ``_443._tcp``. In general, a subname consists of lowercase alphanumeric characters as well as hyphens ``-``, underscores ``_``, and dots ``.``. Wildcard name components are denoted by ``*``; this is allowed only once at the beginning of the name (see RFC 4592 for details). The maximum length is 178. Further restrictions may apply on a per-user basis. Note that for subnames to be created, they must be explicitly stated. In particular, the ``www`` name is not automatically created when assigning an IP address to your domain name (by creating an ``A`` or ``AAAA`` record). The same applies for the catch-all mechanism: If you would like a record to apply to all otherwise undefined subdomains, the wildcard subdomain ``*`` must be explicitly given. ``ttl`` :Access mode: read, write TTL (time-to-live) value, which dictates for how long resolvers may cache this RRset, measured in seconds. The smallest acceptable value is given by the domain's :ref:`minimum TTL ` setting. The maximum value is 86400 (one day). ``type`` :Access mode: read, write-once (upon RRset creation) RRset type (uppercase). A broad range of record types is supported, with most DNSSEC-related types (and the ``SOA`` type) managed automagically by the backend. For details, check the section on `Record Types`_. ``touched`` :Access mode: read-only Timestamp of when the RRset was last touched (same format as ``created``). This field reflects the most recent write request to the RRset. It is also updated when the write request does not actually change anything (e.g. overwriting a DNS record with identical values). .. _creating-an-rrset: Creating an RRset ~~~~~~~~~~~~~~~~~ To create a new RRset, simply issue a ``POST`` request to the ``/api/v1/domains/{name}/rrsets/`` endpoint, like this:: curl https://desec.io/api/v1/domains/{name}/rrsets/ \ --header "Authorization: Token {secret}" \ --header "Content-Type: application/json" --data @- <<< \ '{"subname": "www", "type": "A", "ttl": 3600, "records": ["127.0.0.1", "127.0.0.2"]}' ``type``, ``records``, and ``ttl`` are mandatory, whereas the ``subname`` field is optional. Upon success, the response status code will be ``201 Created``, with the RRset contained in the response body. If the operation cannot be performed with the given parameters, the API returns ``400 Bad Request``. This can happen, for instance, when there is a conflicting RRset with the same name and type, when not all required fields were provided correctly (such as, when the ``type`` value was not provided in uppercase), or when the record content is semantically invalid (e.g. when you provide an unknown record type, or an ``A`` value that is not an IPv4 address). Note that the values of ``type`` and ``subname`` as well as the ``records`` items are strings, and as such the JSON specification requires them to be enclosed in double quotes (with the quotes being part of the field value); your shell or programming language may require another layer of quotes! By contrast, ``ttl`` is an integer field, so the JSON value does not contain quotes. RRset write operations are subject to rate limiting (see :ref:`rate-limits`). When creating (or updating) a large number of RRsets, we strongly encourage you to use `Bulk Operations`_ instead of multiple sequential requests. (Single requests are much more expensive on the server side, as each request triggers a DNSSEC signing operation. With bulk requests, only one signing operation is performed, covering all changes together.) Creating a TLSA RRset ````````````````````` A common use case is the creation of a ``TLSA`` RRset which carries information about the TLS certificate used by the server that the domain points to. For example, to create a ``TLSA`` RRset for ``www.example.com``, you can run:: curl https://desec.io/api/v1/domains/{name}/rrsets/ \ --header "Authorization: Token {secret}" \ --header "Content-Type: application/json" --data @- <&1 \ | cut -f2 -d ' ') Alternatively, you can use a tool like https://www.huque.com/bin/gen_tlsa. For full detail on how ``TLSA`` records work, please refer to RFC 6698. Bulk Creation of RRsets ``````````````````````` It is often desirable to create several RRsets at once. This is achieved by sending an array of RRset objects to the ``rrsets/`` endpoint (instead of just one), like this:: curl https://desec.io/api/v1/domains/{name}/rrsets/ \ --header "Authorization: Token {secret}" \ --header "Content-Type: application/json" --data @- <; rel="first", ; rel="prev", ; rel="next" where ``:prev_cursor`` and ``:next_cursor`` are page identifiers that are to be treated as opaque by clients. On the first/last page, the ``Link:`` header will not contain a ``prev``/``next`` link, respectively. If no pagination parameter is given although pagination is required, the server will return ``400 Bad Request``, along with a ``Link:`` header containing the ``first`` link, and human-readable instructions on pagination in the body. Filtering by Record Type ```````````````````````` To retrieve an array of all RRsets from your zone that have a specific type (e.g. all ``A`` records, regardless of ``subname``), augment the previous ``GET`` request with a ``type`` query parameter carrying the desired RRset type like:: curl https://desec.io/api/v1/domains/{name}/rrsets/?type={type} \ --header "Authorization: Token {secret}" Query parameters used for filtering are fully compatible with `pagination`_. Filtering by Subname ```````````````````` To filter the RRsets array by subname (e.g. to retrieve all records in the ``www`` subdomain, regardless of their type), use the ``subname`` query parameter, like this:: curl https://desec.io/api/v1/domains/{name}/rrsets/?subname={subname} \ --header "Authorization: Token {secret}" This approach also allows to retrieve all records associated with the zone apex (i.e. ``example.com`` where ``subname`` is empty), by querying ``rrsets/?subname=``. Query parameters used for filtering are fully compatible with `pagination`_. Retrieving a Specific RRset ~~~~~~~~~~~~~~~~~~~~~~~~~~~ To retrieve an RRset with a specific name and type from your zone (e.g. the ``A`` record for the ``www`` subdomain), issue a ``GET`` request with the ``subname`` information and the type appended to the ``rrsets/`` endpoint, like this:: curl https://desec.io/api/v1/domains/{name}/rrsets/{subname}/{type}/ \ --header "Authorization: Token {secret}" This will return only one RRset (i.e., the response is not a JSON array). The response status code is ``200 OK`` if the requested RRset exists, and ``404 Not Found`` otherwise. Accessing the Zone Apex ``````````````````````` **Note:** The RRset at the zone apex (the domain root with an empty subname) *cannot* be queried via ``/api/v1/domains/{name}/rrsets//{type}/``. This is due to normalization rules of the HTTP specification which cause the double-slash ``//`` to be replaced with a single slash ``/``, breaking the URL structure. To access an RRset at the root of your domain, we reserved the special subname value ``@``. This is a common placeholder for this use case (see RFC 1035). As an example, you can retrieve the IPv4 address(es) of your domain root by running:: curl https://desec.io/api/v1/domains/{name}/rrsets/@/A/ \ --header "Authorization: Token {secret}" **Pro tip:** If you like to have the convenience of simple string expansion in the URL, you can add three dots after ``{subname}``, like so:: curl https://desec.io/api/v1/domains/{name}/rrsets/{subname}.../{type}/ \ --header "Authorization: Token {secret}" With this syntax, the above-mentioned normalization problem does not occur, and no special treatment is needed for accessing the zone apex. You can think of the three dots as abbreviating the rest of the DNS name. Modifying an RRset ~~~~~~~~~~~~~~~~~~ To modify an RRset, use the endpoint that you would also use to retrieve that specific RRset. The API allows changing the values of ``records`` and ``ttl``. When using the ``PATCH`` method, only fields you would like to modify need to be provided. In contrast, if you use ``PUT``, the full resource must be specified (that is, all fields, including ``subname`` and ``type``). Examples:: curl -X PUT https://desec.io/api/v1/domains/{name}/rrsets/{subname}/{type}/ \ --header "Authorization: Token {secret}" \ --header "Content-Type: application/json" --data @- <