Skip to main content

Authentication

Every request is authenticated with an API key sent in the Authorization header, using the Bearer scheme.

curl https://api.joinotify.com/templates \
-H 'Authorization: Bearer sk_live_xxx'

Token format​

sk_live_AbCdEf0123456789abcdefgh
│ │ └── 32 random characters (192 bits of entropy)
│ └── environment: live or test
└── fixed prefix

The first 14 characters form the prefix (sk_live_AbCdEf). It is shown in the dashboard and in listings so you can identify a key without revealing the secret.

The token never goes in the URL

Meta's documentation shows examples with ?access_token= in the query string. That does not work on Joinotify and never will — URL parameters leak into server logs, browser history and Referer headers.

# Correct
curl https://api.joinotify.com/v1/{WABA_ID} \
-H 'Authorization: Bearer sk_live_xxx'

# Does not work
curl 'https://api.joinotify.com/v1/{WABA_ID}?access_token=sk_live_xxx'

Creating a key​

Through the dashboard, under API keys, or through the API itself with an existing key:

curl -X POST https://api.joinotify.com/keys \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "name": "erp-integration", "environment": "live" }'
201 Created
{
"data": {
"id": "key_01J8ZK...",
"name": "erp-integration",
"prefix": "sk_live_AbCdEf",
"environment": "live",
"createdAt": "2026-07-27T14:02:11.482Z",
"secret": "sk_live_AbCdEf0123456789abcdefgh"
},
"warning": "Store the \"secret\" now — it will not be shown again."
}

The secret field appears only in that response. We store only the SHA-256 hash of the secret — there is no way to recover it later.

Editing a key​

Rename the key, or change which numbers it reaches, without swapping the secret:

curl -X PATCH https://api.joinotify.com/keys/key_01J8ZK... -H 'Authorization: Bearer sk_live_xxx' -H 'Content-Type: application/json' -d '{ "allowedPhoneNumberIds": ["1555000111", "1555000222"] }'

This is the path for whoever connected a second WhatsApp and wants the key already in production to send through it too: the sk_… stays the same, so no .env, plugin or no-code flow has to be updated. The new reach applies from the very next request.

allowedPhoneNumberIds replaces the whole list — send the numbers the key should reach, not just the new ones. An empty list gives the key every number in the account, including the ones you connect later. Omitting the field leaves the reach untouched.

environment is not editable: it is baked into the secret itself (sk_live_… / sk_test_…). To switch environments, create another key. An already revoked key returns 409 key_revoked — revocation has no undo.

Listing and revoking​

# List (never returns secrets)
curl https://api.joinotify.com/keys \
-H 'Authorization: Bearer sk_live_xxx'

# Revoke
curl -X DELETE https://api.joinotify.com/keys/key_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx'

Revoking takes effect immediately: the next request with that key already gets 401 authentication. Revoking an already revoked key returns 200 — the operation is idempotent, so repeating it breaks nothing.

Good practice​

One key per integration. ERP, storefront and chatbot with separate keys. When one leaks, you revoke only that one.

Rotate periodically. Create the new key, publish it, confirm traffic has moved (the dashboard shows lastUsedAt per key) and only then revoke the old one. Since both coexist, rotation has no downtime.

Never in the front end. The key grants full access to your account. It lives on the server, in an environment variable or a secrets manager — never in browser code, a mobile app or a repository.

Leaked? Revoke first. Revoking takes a second; investigating can take hours.

Authentication errors​

HTTP codetypeCause
401authenticationMissing header, invalid format, unknown or revoked key.
403tenant_inactiveThe account is suspended or cancelled. Contact support.
401 Unauthorized
{
"error": {
"message": "Invalid API key.",
"type": "authentication"
}
}

The live and test environments​

Keys can be created as live or test. Today the prefix exists so you can tell credentials apart in your own systems and logs.

There is no sandbox

There is no isolated test environment: an sk_test_ key sends real messages and is billed like any other. To test without impact, use a destination number you control.