NodukanNodukan/ Developers
Introduction

Authentication

How Nodukan API keys work — token format, Bearer auth, test vs live environments, scopes, key rotation, and IP whitelisting.

Authentication

All Nodukan API requests are authenticated using long-lived API keys transmitted as Bearer tokens. There are no session cookies, OAuth redirects, or refresh token flows — just a key you keep secure and send on every request.

Token format#

Every API key follows this structure:

NDK_<env>_<kid16>.<secret32>
ComponentDescription
NDKConstant prefix — identifies this as a Nodukan Developer Key
<env>live for production, test for development/staging
<kid16>16-character key identifier used for lookup (URL-safe base62)
<secret32>32-character secret used for HMAC signature verification

Example:

NDK_live_k8f3mxqt2vrz9ywn.a7d4e1b9c2f0g5h6j3k8m2p4q7r1s9t0

The server splits the token on ., looks up the key by kid, hashes the provided secret with bcrypt (or compares via constant-time HMAC), and validates scopes before processing the request.

Sending the token#

Include your key in the Authorization header on every request:

curl https://api.nodukan.com/v1/account \
  -H "Authorization: Bearer NDK_live_k8f3mxqt2vrz9ywn.a7d4e1b9c2f0g5h6j3k8m2p4q7r1s9t0"

In Node.js:

const API_KEY = process.env.NODUKAN_API_KEY; // NDK_live_...

const res = await fetch('https://api.nodukan.com/v1/lead-magnets', {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
});

In Python:

import os, requests

API_KEY = os.environ["NODUKAN_API_KEY"]

resp = requests.get(
    "https://api.nodukan.com/v1/lead-magnets",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
data = resp.json()
Warning:

Never expose your API key in client-side JavaScript, mobile app source code, public repositories, or log files. Treat it with the same care as a database password.

Test vs Live keys#

Key typePrefixBehavior
LiveNDK_live_Real data, live actions (actual posts published, real leads recorded)
TestNDK_test_Sandboxed data, no real side effects (posts don't publish, leads aren't recorded)

Use NDK_test_ keys in CI pipelines, local development, and staging environments. Live and test keys are managed separately in the dashboard and have independent scope configurations.

Scopes#

Scopes limit what a key can do. You assign scopes when you create a key; they cannot be expanded later without rotating the key. Prefer the principle of least privilege — grant only the scopes your integration needs.

ScopeGrants
account:readRead account profile and subscription details
lead_magnets:readList and read lead magnets and their captured leads
lead_magnets:writeCreate and update lead magnets
leads:readRead contact details from lead submissions
short_urls:readList and read short URL records and analytics
short_urls:writeCreate and update short URLs
social:readRead social connections, posts, and insights
social:writeSchedule, publish, and delete social posts; manage automations
chats:readList conversations and read chat message history
chats:writeSend replies to chat conversations
webhooks:readList and read webhook endpoint configurations
webhooks:writeCreate, update, delete, and test webhook endpoints

Scope format in errors

When a request fails due to insufficient scope, the 403 response body includes:

{
  "error": {
    "type": "permission_error",
    "code": "insufficient_scope",
    "message": "This key does not have the 'lead_magnets:write' scope.",
    "requiredScopes": ["lead_magnets:write"],
    "grantedScopes": ["lead_magnets:read", "account:read"]
  },
  "requestId": "req_01j9xabc..."
}

Rotating keys#

Key rotation is a security best practice. To rotate a key:

  1. In the dashboard, navigate to API Keys → Your Key → Rotate
  2. A new key is generated with the same name and scopes
  3. The old key continues to work for a 24-hour grace period while you deploy the new key
  4. After 24 hours, the old key is permanently revoked

You can also revoke a key immediately without generating a replacement.

IP whitelisting#

For additional security, you can restrict a key to specific IP addresses or CIDR ranges. Requests from unlisted IPs receive:

{
  "error": {
    "type": "authentication_error",
    "code": "ip_not_allowed",
    "message": "The request originated from an IP address not in this key's allowlist."
  },
  "requestId": "req_01j9x..."
}

Configure IP allowlists in Dashboard → API Keys → Your Key → IP Restrictions.

Note:

IP whitelisting is available on the Pro plan and above. It's especially recommended for server-to-server integrations where the source IP is static.

Key expiry#

You can set an optional expiry date when creating a key. Once expired, all requests with that key return a 401 key_expired error. Expired keys cannot be reactivated — generate a new key if needed.

Security checklist#

  • ✅ Store keys in environment variables or a secrets manager (AWS Secrets Manager, Vault, Doppler)
  • ✅ Use NDK_test_ keys in development and CI
  • ✅ Grant the minimum scopes required for each integration
  • ✅ Enable IP whitelisting for server-side integrations with static IPs
  • ✅ Rotate keys every 90 days as part of your security routine
  • ✅ Audit API key activity in Dashboard → Audit Logs
  • ❌ Do not embed keys in frontend JavaScript bundles
  • ❌ Do not commit keys to git, even in private repositories
  • ❌ Do not share keys across multiple integrations — use one key per integration
Was this helpful?
Edit on GitHub

Last updated