API reference
The DNS by echo reply REST API is offered with the Enterprise plan. It uses the same users, organizations, roles, and permissions as the web application. Contact info@echo-reply.net to discuss access.
This page is a practical introduction to the DNS endpoints. The application’s generated documentation is the canonical contract for every field and route:
Base URL and versioning
The production base URL already includes the current API version:
https://dns.echo-reply.net/api/v1/
Append paths such as orgs/{org_slug}/zones/ to that URL. Do not add a second
/v1. Paths in this reference include their trailing slash because the API’s
routes are defined that way.
Authentication and access
Send a user token with the Token authentication scheme:
Authorization: Token YOUR_TOKEN
This is not a Bearer token. A token belongs to one user and inherits that
user’s organization memberships and role permissions. Tokens do not have
independent zone scopes. For example, listing zones requires the user’s
can_view_domains permission, while creating or changing one requires
can_edit_domains.
Create, rotate, or revoke a token
While signed in with a browser session, create a token with:
POST https://dns.echo-reply.net/api/v1/auth/token/
{
"token": "0123456789abcdef0123456789abcdef01234567",
"created": "2026-09-14T18:20:00Z"
}
Calling the same endpoint again while authenticated—by session or by the existing token—rotates the token and immediately revokes the old one. Revoke the current user’s token with:
DELETE https://dns.echo-reply.net/api/v1/auth/token/
A successful revocation returns 204 No Content. Store tokens like passwords,
never put them in URLs or client-side code, and rotate them if exposed.
Organization scoping
Most resources are scoped by the organization slug:
https://dns.echo-reply.net/api/v1/orgs/{org_slug}/...
The organization must exist, the authenticated user must be a member, and the user’s role must allow the requested action. A token cannot be used to cross those tenant or role boundaries.
Zones
List zones
GET https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/?page=1&ordering=name
Authorization: Token YOUR_TOKEN
Accept: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 42,
"name": "example.dk",
"type": "MASTER",
"serial": "2026091401",
"disabled": false,
"dnssec_enabled": true,
"created": "2026-09-14T18:20:00Z",
"modified": "2026-09-14T18:22:00Z"
}
]
}
Supported filters are name (case-insensitive contains), type, disabled,
and dnssec. Supported ordering fields are name, created, and modified.
prefix a field with - for descending order.
Retrieve a zone
GET https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/42/
Authorization: Token YOUR_TOKEN
The detail response includes the zone’s SOA information, DS data when DNSSEC is enabled, and its record count in addition to the list fields.
Create a zone
POST https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/
Authorization: Token YOUR_TOKEN
Content-Type: application/json
{
"name": "example.dk"
}
name is required. The application defaults a directly created served zone to
MASTER and uses the organization email as the SOA administrator address when
admin_email is omitted. See the OpenAPI schema for optional secondary-zone,
automatic PTR, and disabled-state fields.
An authorized direct change returns 201 Created with the created zone. If the
user may propose changes but cannot self-approve them, the same request returns
202 Accepted instead:
{
"detail": "Your changes have been submitted for admin approval.",
"stashed_change": {
"id": 317,
"change_type": "CREATE",
"status": "PENDING",
"content_type": "domain",
"object_id": null,
"object_repr": "example.dk",
"changes": {
"name": "example.dk",
"organization": 12,
"admin_email": "dns-admin@example.dk"
},
"created_by": "operator@example.dk",
"created_at": "2026-09-14T18:20:00Z",
"reviewed_by": null,
"reviewed_at": null,
"review_notes": ""
}
}
Update or delete a zone
Use PATCH for a partial update, PUT for a complete update, and DELETE to
remove a zone:
PATCH https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/42/
Authorization: Token YOUR_TOKEN
Content-Type: application/json
{
"disabled": true
}
DELETE https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/42/
Authorization: Token YOUR_TOKEN
A direct delete returns 204 No Content. A change requiring approval returns
202 Accepted with a pending stashed_change, as creation does. Deletion is
destructive once applied.
DNSSEC has explicit actions at
POST .../zones/{zone_id}/enable-dnssec/ and
POST .../zones/{zone_id}/disable-dnssec/.
Records
Record names are fully qualified and do not end in a dot. For example, use
www.example.dk, not www or www.example.dk..
List records in a zone
GET https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/42/records/?type=A&page=1
Authorization: Token YOUR_TOKEN
Accept: application/json
Supported filters are name (case-insensitive contains), type, content
(case-insensitive contains), and disabled. Supported ordering fields are
name, type, ttl, created, and modified.
For a read-only view across every zone in the organization, use:
GET https://dns.echo-reply.net/api/v1/orgs/acme-network/records/
Create a record
POST https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/42/records/
Authorization: Token YOUR_TOKEN
Content-Type: application/json
{
"name": "www.example.dk",
"type": "A",
"ttl": 300,
"content": "192.0.2.10"
}
A directly created record returns 201 Created:
{
"id": 901,
"name": "www.example.dk",
"type": "A",
"content": "192.0.2.10",
"ttl": 300,
"prio": null,
"disabled": false,
"remarks": null,
"domain": {
"id": 42,
"name": "example.dk"
},
"change_date": 1789410000,
"created": "2026-09-14T18:20:00Z",
"modified": "2026-09-14T18:20:00Z"
}
If ttl is omitted, the zone’s SOA minimum is used when available. prio is
used by record types such as MX. Supported record types and their validation
rules are defined in the generated OpenAPI schema.
Update and delete routes are:
PATCH https://dns.echo-reply.net/api/v1/orgs/{org_slug}/zones/{zone_id}/records/{record_id}/
DELETE https://dns.echo-reply.net/api/v1/orgs/{org_slug}/zones/{zone_id}/records/{record_id}/
As with zones, these operations return the result immediately for users who
can self-approve, or 202 Accepted with a pending change where approval is
required.
Approvals and asynchronous operations
A 202 Accepted DNS write has not changed live DNS. It created a pending
proposal. Read proposals at:
GET https://dns.echo-reply.net/api/v1/orgs/{org_slug}/changes/
GET https://dns.echo-reply.net/api/v1/orgs/{org_slug}/changes/{change_id}/
POST https://dns.echo-reply.net/api/v1/orgs/{org_slug}/changes/{change_id}/approve/
POST https://dns.echo-reply.net/api/v1/orgs/{org_slug}/changes/{change_id}/reject/
POST https://dns.echo-reply.net/api/v1/orgs/{org_slug}/changes/{change_id}/cancel/
Approval and rejection accept an optional review_notes string. Role rules
still apply: an ordinary proposer cannot approve their own request merely by
calling the approval route.
Zone and record CRUD either completes synchronously or enters this approval workflow. Domain registration and registrar operations have separate stateful and asynchronous outcomes. Use the registration and transaction resources in the canonical API documentation, and treat a submitted or processing result as pending until the corresponding transaction reaches a terminal state.
Pagination
List endpoints use page-number pagination with 50 results per page:
GET https://dns.echo-reply.net/api/v1/orgs/acme-network/zones/?page=2
Responses contain count, next, previous, and results. Follow the
absolute next and previous URLs returned by the server. A client-selectable
page size is not part of the current API contract.
Rate limits
Authenticated requests are limited to 200 requests per minute per user and also to a 20 requests per second burst. Some routes are stricter. For example, domain availability search is limited to 30 requests per minute and organization creation to 5 requests per day.
When a limit is exceeded, the API returns 429 Too Many Requests and a
Retry-After header. Wait for that interval before retrying. The API does not
promise X-RateLimit-* headers on every response.
Plans and resource limits
API access is commercially packaged with Enterprise, while technical authorization is enforced through the authenticated user and organization RBAC. Enterprise-only resources, such as creating private masters, also have an explicit entitlement check.
Organization resource limits apply equally to the web application and API.
Creating a zone when its limit has been reached returns 403 Forbidden. For a
direct record write over quota, the record may be created disabled with a
quota_warning. A proposed record write can instead return 403 Forbidden.
Clients must inspect both the HTTP status and response body.
Errors
Validation errors are keyed by field and contain arrays of messages:
{
"name": ["This field is required."]
}
Authentication, permission, lookup, throttling, and other API errors normally
use detail and may include a machine-readable code:
{
"detail": "Invalid token.",
"code": "authentication_failed"
}
Common status codes are:
| Status | Meaning |
|---|---|
400 Bad Request | Invalid fields or an operation rejected by application rules |
401 Unauthorized | Missing, invalid, revoked, or incorrectly prefixed token |
403 Forbidden | The user lacks an organization permission, entitlement, or available resource |
404 Not Found | The organization, zone, record, or other resource is not visible or does not exist |
409 Conflict | The resource state conflicts with the action, such as approving a completed proposal |
429 Too Many Requests | A request throttle was exceeded. Honor Retry-After |
500 Internal Server Error | Unexpected server failure. Retry cautiously and contact support if it persists |
Do not rely on one universal error code or envelope for every validation and application-level response. Preserve the HTTP status and body in client logs, without logging tokens or sensitive record content.