Introducing PostScale -- email API for transactional, inbound, and masked addresses. PostScale

    Zone-scoped API keys — a smaller blast radius for DNS automation

    Published on April 22nd, 2026

    Create DNScale API keys that can only touch the zones you pick. Ship safer CI pipelines, grant per-brand access, and keep the blast radius of a leaked key small.

    DNScale API keys have always been customer-wide. If you handed a key to a CI pipeline, a teammate, or a partner agency, that key could read and write every zone on the account. Fine for small setups, uncomfortable once you run a few different brands, clients, or environments behind a single DNScale account.

    Today we're shipping zone-scoped API keys. You pick the zones a key is allowed to touch at creation time. Everything outside that set is invisible to the key — not just read-only, literally 404 / 403.

    Why we built it

    A few patterns kept coming up:

    • CI for one app shouldn't manage DNS for the whole company. A staging-deploy pipeline for api.staging.acme.app has no business editing records on payroll.acme.corp, but with a customer-wide key one leaked CI secret exposed both.
    • Agencies managing DNS for multiple clients want to give each client a key that only sees their own zones — no cross-client visibility, no mistakes during script execution.
    • Third-party tooling (monitoring, IaC modules, webhook handlers) rarely needs access to anything beyond the zone it's provisioning for. Least privilege should be the default, not a best-effort convention.

    Zone scope is the missing knob.

    How it works

    When you create an API key in the dashboard, there's a new section:

    Zone scope ◉ All zones — key can access every zone in this account. ○ Specific zones — key is restricted to the zones you pick.

    Pick Specific zones, filter the list, tick the zones you want. The key you get back only works for those zones. Existing keys are unaffected — they remain customer-wide unless you create new scoped keys alongside them.

    Under the hood, the zone set is stored in a join table (api_key_zones) and enforced by middleware on every request before scope checks run. A zone-scoped key hitting an out-of-scope zone gets a 403, not a "no such zone" — we tell you it's a scope issue so you can fix it.

    What changes for a zone-scoped key

    Zone scope layers on top of the existing scopes enum (zones:read, records:write, and friends). A zone-scoped key still needs the right scopes to do anything; it just can't reach across zones.

    EndpointCustomer-wide keyZone-scoped key
    GET /zoneslists all zoneslists only the scoped zones
    GET /zones/:idany zone in the accountin-scope zones only — 403 otherwise
    POST /zones/:id/recordsany zonein-scope zones only
    GET /zones/:id/dnssecany zonein-scope zones only
    POST /zones (create new zone)alloweddenied 403
    /usage/*, /billing/*alloweddenied 403
    /users/*, /apikeys/*alloweddenied 403

    The rule of thumb: a zone-scoped key can do anything a normal key can do, provided the request names a zone in its set. Anything that would widen the scope (creating a new zone, listing users, managing other API keys, reading customer-wide usage or billing) is off-limits — those paths would effectively let a scoped key escape its sandbox.

    One subtlety worth calling out: the zone boundary beats the admin scope. An admin+zone-scoped key is still confined to its zones. Admin scope bypasses scope checks, not zone boundaries. That's the whole point of the feature.

    A concrete example

    Say you run acme.com, acme.dev, and a dozen client zones in the same DNScale account. You're wiring up Terraform for your staging environment and want a key that only acme.dev CI can use.

    1. Dashboard → API Keys → Generate API Key.
    2. Name it ci-staging-acme-dev. Tick zones:read, records:read, records:write.
    3. Zone scope: Specific zones → tick acme.dev.
    4. Create, copy the key into your CI secret store.

    Now:

    # This works — acme.dev is in scope.
    curl -H "Authorization: Bearer $CI_KEY" \
      https://api.dnscale.eu/v1/zones/<acme-dev-id>/records
     
    # This 403s — acme.com is outside the scope.
    curl -H "Authorization: Bearer $CI_KEY" \
      https://api.dnscale.eu/v1/zones/<acme-com-id>/records
     
    # This 403s — creating new zones would escape scope.
    curl -H "Authorization: Bearer $CI_KEY" \
      -X POST https://api.dnscale.eu/v1/zones \
      -d '{"name":"new-zone.com"}'
     
    # GET /zones returns only acme.dev — acme.com and client zones are hidden.
    curl -H "Authorization: Bearer $CI_KEY" https://api.dnscale.eu/v1/zones

    If $CI_KEY leaks, the blast radius is one zone instead of your entire account. Rotate that one key, done.

    Patterns we'd recommend

    • One key per pipeline, per environment. ci-staging-acme-dev, ci-prod-acme-com, terraform-client-x. Small keys with narrow names beat big keys with broad names every time.
    • Pair zone scope with narrow permissions. A CI key that only touches TXT records for DNS-01 validation needs records:read + records:write on one zone — not zones:write, not dnssec:write.
    • Keep one admin-ish customer-wide key for humans and ops tooling, scope everything else. Nothing prevents you from using both.
    • Audit regularly. The dashboard shows "All zones" vs "Scoped to N zones" in the key list. If a key that shouldn't have customer-wide reach still shows "All zones", that's your signal to replace it.

    Rollout and compatibility

    Zone scoping is locked at creation time in this first version — you pick the zones when the key is generated, and they stay fixed for the life of the key. If you need to change the scope, create a new key and rotate. We'll look at editable scope once the patterns settle.

    Existing API keys are not touched. If you never create a scoped key, nothing about your integration changes.


    Zone scoping is live now. If you've been holding off on handing an API key to a pipeline, a client, or an automation script because it felt too broad, this is the version to try.