Create a node and its one-time enrolment code.
const url = 'https://api.opdns.io/v1/profiles/example/nodes';const options = { method: 'POST', headers: { cookie: 'opdns_session=<opdns_session>', 'Content-Type': 'application/json' }, body: '{"name":"node"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.opdns.io/v1/profiles/example/nodes \ --header 'Content-Type: application/json' \ --cookie opdns_session=<opdns_session> \ --data '{ "name": "node" }'Token scope: profiles:write. Returns the enrolment code once; it expires after
15 minutes and works once. The node exchanges it at POST /v1/nodes/enrol.
Does not change the profile version.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Public 6-character id; never 000000.
Header Parameters
Section titled “Header Parameters”Makes the POST safe to retry: a repeat with the same key and the same request
within 24 h returns the stored response (with Idempotent-Replayed: true)
without creating again. 1 to 255 visible ASCII characters (a UUID is fine).
Request Bodyrequired
Section titled “Request Bodyrequired”object
Responses
Section titled “Responses”Created.
object
object
Public 6-character id; never 000000.
pending until the enrolment code is exchanged.
object
object
Round trip of the link as the node measured it at its last health report (handshake, then keepalive pings). Absent when unknown (older nodes).
Ids of the profile-signing keys the node trusts (configured and learnt), as it reported them in its last health report; the node refuses profiles signed by any other key. Empty when it checks no signature. Absent from nodes that predate the field.
health.link_rtt_ms while the node is online; null when offline or unknown.
health.profile_key_ids, the key ids from the node’s last health report (kept while it is offline); empty when unknown.
Last committed rotation of the node token (node.token_rotated); absent before the first.
Approximate location of a self-hosted node, from GeoIP.
object
Latitude; the country’s representative point when the database has no coordinates.
Longitude, as lat.
ISO 3166-1 alpha-2 country code.
The city, when the database has one (a country database has none).
Where the location comes from.
Only in GET /v1/profiles/{id}/nodes: log batches waiting in the node’s
queue (pending plus delivered but unacknowledged on its consumer, or every
queued batch of the profile before the node first connected). Null when
unknown (revoked node, or the queue could not be read).
One-time enrolment code (opdnsenrol_...), shown once.
Example
{ "node": { "mode": "enrolled", "status": "pending", "location": { "source": "geoip" } }}Headers
Section titled “Headers”Malformed request.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/bad_request", "title": "Bad Request", "status": 400, "code": "bad_request", "detail": "invalid JSON body", "request_id": "5f2c9a0e7b1d4c38"}Not authenticated.
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/unauthenticated", "title": "Unauthorized", "status": 401, "code": "unauthenticated", "detail": "authentication required", "request_id": "5f2c9a0e7b1d4c38"}Authenticated but not allowed: insufficient_scope, session_required, csrf_rejected, mfa_enrolment_required (restricted session), mfa_required (an operator action, or an admin-scoped token, from a session signed in with the password alone; not in dev), reauth_required (sign in again within five minutes), account_pending_deletion (the account is in its deletion cooling-off).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/insufficient_scope", "title": "Forbidden", "status": 403, "code": "insufficient_scope", "detail": "the token lacks the profiles:write scope", "request_id": "5f2c9a0e7b1d4c38"}Not found (also for resources of another organisation).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/not_found", "title": "Not Found", "status": 404, "code": "not_found", "detail": "resource not found", "request_id": "5f2c9a0e7b1d4c38"}Conflict. profile_suspended: the operator suspended the profile; it resolves without filtering and refuses changes until it is reinstated (reads still work). On POSTs with an Idempotency-Key: idempotency_key_reused or idempotency_in_progress (retry after Retry-After).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/profile_suspended", "title": "Conflict", "status": 409, "code": "profile_suspended", "detail": "the profile is suspended by the operator", "request_id": "5f2c9a0e7b1d4c38"}Headers
Section titled “Headers”Field validation failed (validation_failed, unknown_list).
object
Stable machine code.
object
Seconds, repeating the Retry-After header (rate limits, offline nodes).
Example
{ "type": "https://opdns.io/problems/validation_failed", "title": "Unprocessable Content", "status": 422, "code": "validation_failed", "detail": "the request has invalid fields", "errors": [ { "field": "settings.block_mode", "message": "one of nxdomain, refused, null, block-page" } ], "request_id": "5f2c9a0e7b1d4c38"}