Skip to content

Overview

Control plane REST API for opdns profiles, accounts and tokens.

The dashboard is the first client; there are no private endpoints.

Authentication: a session cookie (opdns_session, set by POST /v1/auth/login) or an API token sent as Authorization: Bearer opdns_.... Tokens carry scopes (profiles:read, profiles:write, account:read, logs:read, analytics:read); sessions have every scope. The admin scope (operator routes under /v1/admin) exists only for accounts flagged as operators. Cookie-authenticated writes from another origin are rejected unless the origin is an allowed dashboard origin. The unauthenticated /v1/auth writes (signup, login, verification, password reset, recovery, passkey login) apply the same check: an Origin that is present and not allowed answers 403 csrf_rejected; requests without one are unaffected.

CORS: the four public signup surfaces (/v1/auth/signup, /v1/auth/signup-mode, /v1/waitlist, /v1/waitlist/confirm) allow exact configured origins, their route’s method and Content-Type only. Eligible preflights answer 204; a rejected origin, method or header answers 403. Actual responses name the origin and allow credentials. Other /v1 routes add Access-Control-Expose-Headers: ETag, Retry-After, X-Request-Id, Content-Disposition, Location for an allowed dashboard origin. Their preflights answer 204 without authentication; unallowed origins get no CORS headers. Responses vary on Origin on those routes, and on signup surfaces when Origin is present.

Errors are RFC 9457 problem documents (application/problem+json) with a stable machine code.

Profile writes are optimistic: every profile resource returns an ETag holding the profile version, and writes accept If-Match with it (412 on mismatch). Every write bumps the version and publishes the full profile document to the edge.

Unauthenticated routes are rate limited per client IP (429 with Retry-After).

Lists are cursor-paginated: limit (default 50, at most 200) and cursor (the next_cursor of the previous page, opaque); next_cursor is null on the last page. Order is stable, so rows created between pages are never repeated or skipped before the cursor. A modified cursor is a 400 invalid_cursor.

POSTs that create something take an Idempotency-Key header: a repeat with the same key and request within 24 h replays the first (2xx) response with Idempotent-Replayed: true instead of creating again; the key reused with a different request is a 409 idempotency_key_reused, and a repeat while the first is still running a 409 idempotency_in_progress. Keys are per account.

Sessions last at most 30 days from sign-in and end after 7 days unused; the first request more than 24 h after the session token was issued gets a new token in Set-Cookie (the previous one stays valid for a few seconds).

Credentials: passkeys (WebAuthn) are the primary credential; password sign-in takes a TOTP code when TOTP is on; ten single-use recovery codes are issued with the first second factor. Credential changes need a sign-in within the last five minutes (reauth_required). Emailed links point at the dashboard: /verify-email?token=, /reset-password?token=, /login, /account/security.

Versioning and deprecation. The path carries the major version: every route is under /v1, and within /v1 changes are additive only: new endpoints, new optional request fields, new response fields, new enum values in responses (clients ignore unknown fields and treat unknown enum values as “other”), new columns appended to query results. Nothing is renamed, removed, retyped or made required within /v1; such a change needs /v2, served alongside /v1 until /v1’s sunset. info.version is this document’s version: it changes with every change to the document (minor for additions, patch for wording), and every response carries it in the X-Opdns-Api-Version header (also api_version in GET /v1/meta). A field or endpoint being retired is marked deprecated: true here with its replacement; responses that use it carry Deprecation (RFC 9745: @<unix seconds>, when it was deprecated) and Sunset (RFC 8594: an HTTP date after which it may be removed, at least 90 days after the deprecation), so clients can find the calls that must change before they break.

Security scheme type: apiKey

Cookie parameter name: opdns_session

API token (opdns_...) with scopes.

Security scheme type: http

Node token (opdnsnode_...) from POST /v1/nodes/enrol, for the node-side endpoints.

Security scheme type: http