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>
| Component | Description |
|---|---|
NDK | Constant 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()
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 type | Prefix | Behavior |
|---|---|---|
| Live | NDK_live_ | Real data, live actions (actual posts published, real leads recorded) |
| Test | NDK_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.
| Scope | Grants |
|---|---|
account:read | Read account profile and subscription details |
lead_magnets:read | List and read lead magnets and their captured leads |
lead_magnets:write | Create and update lead magnets |
leads:read | Read contact details from lead submissions |
short_urls:read | List and read short URL records and analytics |
short_urls:write | Create and update short URLs |
social:read | Read social connections, posts, and insights |
social:write | Schedule, publish, and delete social posts; manage automations |
chats:read | List conversations and read chat message history |
chats:write | Send replies to chat conversations |
webhooks:read | List and read webhook endpoint configurations |
webhooks:write | Create, 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:
- In the dashboard, navigate to API Keys → Your Key → Rotate
- A new key is generated with the same name and scopes
- The old key continues to work for a 24-hour grace period while you deploy the new key
- 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.
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
Last updated