---
title: DNS Providers
path: reference/dns-providers
status: published
---

# DNS Providers Reference

Endpoint reference for the DNS provider integrations used by [ACME DNS-01 challenges](../api-guides/acme). ScaiVault writes and cleans up `_acme-challenge` TXT records on your behalf; you configure once which providers manage which zones.

**Base path:** `/v1/dns/providers/`

## Supported providers

| Provider | Type ID | Recommended for |
|----------|---------|-----------------|
| **ScaiDNS** | `scaidns` | ScaiLabs-stack-native deployments (preferred) |
| **Cloudflare** | `cloudflare` | Zones already on Cloudflare |
| **AWS Route 53** | `route53` | AWS-resident deployments |
| **Azure DNS** | `azure` | Azure-resident deployments |
| **Google Cloud DNS** | `google` | GCP-resident deployments |
| **DigitalOcean** | `digitalocean` | DO Networking |
| **PowerDNS** | `powerdns` | Self-hosted PowerDNS with API enabled |

Each provider has a config schema and a credentials schema; the credentials half is encrypted and never returned in responses.

## ScaiDNS

Native ScaiLabs integration. Preferred when ScaiDNS is already in your stack.

Credentials:

| Field | Description |
|-------|-------------|
| `base_url` | ScaiDNS endpoint (`https://scaidns.scailabs.ai` or self-hosted) |
| `api_key` | ScaiDNS API key starting with `sdk_`. Scopes needed: `domains:read`, `records:read`, `records:write` |

Obtain the key from the ScaiDNS admin UI under **Settings → API Keys**. Show-once at creation.

## Cloudflare

Credentials:

| Field | Description |
|-------|-------------|
| `api_token` | API token with `Zone:DNS:Edit` on the relevant zones |

Generate at Cloudflare dashboard → My Profile → API Tokens → Create Token → "Edit zone DNS" template.

## AWS Route 53

Credentials:

| Field | Description |
|-------|-------------|
| `access_key_id` | IAM access key ID (optional if the host runs with a Route 53–capable role) |
| `secret_access_key` | IAM secret access key |
| `region` | Optional, defaults to `us-east-1` |

Minimum IAM policy:

```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "route53:ListHostedZones",
      "route53:GetHostedZone",
      "route53:ListResourceRecordSets",
      "route53:ChangeResourceRecordSets"
    ],
    "Resource": "*"
  }]
}
```

## Azure DNS

Credentials:

| Field | Description |
|-------|-------------|
| `subscription_id` | Azure subscription |
| `resource_group` | Resource group containing the DNS zone |
| `tenant_id` | Azure AD tenant |
| `client_id` | Service principal client ID |
| `client_secret` | Service principal client secret |

The service principal needs the **DNS Zone Contributor** role on the zone resource.

## Google Cloud DNS

Credentials:

| Field | Description |
|-------|-------------|
| `project_id` | GCP project |
| `service_account_json` | Full JSON content of a service account key |

The service account needs the **DNS Administrator** role on the project (or `roles/dns.admin` scoped to specific zones).

## DigitalOcean

Credentials:

| Field | Description |
|-------|-------------|
| `api_token` | Personal access token with read/write scope |

## PowerDNS

Credentials:

| Field | Description |
|-------|-------------|
| `api_url` | PowerDNS API endpoint, e.g. `http://pdns.local:8081` |
| `api_key` | Value of `api-key=` from `pdns.conf` |
| `server_id` | Server identifier, default `localhost` |

## Endpoints

### GET /v1/dns/providers/types

Catalog of supported provider types and their credential schemas. Use for building UIs.

```json
{
  "types": [
    {"id": "scaidns", "name": "ScaiDNS", "credentials_schema": {...}},
    {"id": "cloudflare", "name": "Cloudflare", "credentials_schema": {...}},
    ...
  ]
}
```

**Scope:** authenticated.

### GET /v1/dns/providers

List configured providers in the current tenant.

Response:

```json
{
  "data": [
    {
      "id": "dnsp_abc",
      "name": "ScaiDNS Production",
      "provider_type": "scaidns",
      "managed_zones": ["acme.example", "*.acme.example"],
      "description": "...",
      "is_active": true,
      "last_verified_at": "...",
      "created_at": "..."
    }
  ],
  "has_more": false
}
```

Credentials are never returned. Use `POST /verify` to confirm they work.

**Scope:** `pki:admin`.

### POST /v1/dns/providers

Add a provider.

Body:

| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Tenant-unique |
| `provider_type` | Yes | One of the IDs above |
| `credentials` | Yes | Provider-specific shape |
| `managed_zones` | No | Array of zones this provider serves; empty = auto-discover |
| `description` | No | |
| `verify` | No | Default `true` — refuse to create if credentials fail |

Response `201 Created`: the new provider (no credentials echoed).

**Scope:** `pki:admin`.

### GET /v1/dns/providers/{id}

### PATCH /v1/dns/providers/{id}

Update name, managed zones, description, or credentials.

### DELETE /v1/dns/providers/{id}

Refuses if active ACME orders reference this provider. Disable, complete or cancel orders, then delete.

### POST /v1/dns/providers/{id}/verify

Test the credentials by listing zones from the provider.

Response:

```json
{
  "valid": true,
  "discovered_zones": ["acme.example", "scaivault.example"],
  "checked_at": "..."
}
```

On failure:

```json
{
  "valid": false,
  "error": "401 Unauthorized — API key invalid or expired"
}
```

### GET /v1/dns/providers/{id}/zones

List zones the provider has access to. Useful when populating `managed_zones`.

## Managed zones

`managed_zones` is an ordered list of glob patterns. On an ACME challenge for a domain, ScaiVault picks the first provider whose `managed_zones` includes a matching pattern.

| Pattern | Matches |
|---------|---------|
| `acme.example` | Exact `acme.example` only |
| `*.acme.example` | All subdomains (`api.acme.example`, `www.acme.example`) — but **not** `acme.example` itself |
| `**.acme.example` | All subdomains plus `acme.example` |
| (empty list) | Whatever the provider auto-discovers via its API |

For wildcards (`*.acme.example`) ACME requires `dns-01` and the provider must support managing records at the zone apex.

## How challenges flow

```mermaid
sequenceDiagram
    participant SV as ScaiVault
    participant ACME as ACME server
    participant Picker as Provider selector
    participant DNS as DNS provider
    participant NS as Authoritative NS

    SV->>ACME: order cert for api.acme.example
    ACME-->>SV: TXT _acme-challenge.api.acme.example = token
    SV->>Picker: match api.acme.example against managed_zones
    Picker-->>SV: first match wins
    SV->>DNS: create TXT record
    DNS-->>SV: ok
    loop until propagated or 300s
        SV->>NS: dig TXT _acme-challenge...
        NS-->>SV: not yet / found
    end
    SV->>ACME: challenge ready
    ACME-->>SV: order valid + cert
    SV->>DNS: delete TXT record
```

1. ScaiVault asks the ACME server for a challenge on `api.acme.example`.
2. ACME returns: "Create TXT `_acme-challenge.api.acme.example` = `<token>`".
3. ScaiVault checks active DNS providers' `managed_zones` against `api.acme.example`. First match wins.
4. ScaiVault calls the provider's API to create the TXT record.
5. ScaiVault polls authoritative nameservers for the TXT (timeout: 300 seconds by default).
6. ScaiVault tells ACME the challenge is ready.
7. ACME validates, issues the cert.
8. ScaiVault deletes the TXT record.

Audit log entries cover provider creation, credential verification, every record create/delete, and challenge completion. Look under `action=dns_record_create` / `dns_record_delete`.

## Failure modes

**"No DNS provider configured for domain."** Add a provider with `managed_zones` covering the domain, or expand an existing one.

**Verification keeps failing.** Use `POST /verify` directly to see the upstream error. Common: expired API key, missing scope, region/zone-resource-group mismatch.

**Propagation timeout.** Either the provider isn't actually writing the record (check audit), or the recursive resolver path between ScaiVault and the authoritative nameserver is broken. Reduce the zone's NS TTL during testing to speed up debugging.

**DNSSEC denial.** Some ACME validators perform DNSSEC validation. If the zone has DNSSEC enabled but signing is misconfigured, challenges fail with NXDOMAIN. Verify with `dig +dnssec _acme-challenge.<domain> @<authoritative>`.

## Related

- [ACME](../api-guides/acme) — issuing certificates with DNS-01 challenges.
- [PKI Reference](./pki) — full PKI endpoints.
